@symbiote-native/angular 0.8.0 → 2.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 +13 -10
- package/babel-register-composed.cjs +58 -18
- package/bin/symbiote-angular-dev.cjs +16 -4
- package/build/angular/anchor-host-registry.js +1 -15
- package/build/angular/anchor-host-registry.js.map +1 -1
- package/build/angular/change-detection-flush.d.ts +31 -0
- package/build/angular/change-detection-flush.js +110 -0
- package/build/angular/change-detection-flush.js.map +1 -0
- package/build/angular/components/activity-indicator-props.d.ts +2 -0
- package/build/angular/components/activity-indicator-props.js +2 -0
- package/build/angular/components/activity-indicator-props.js.map +1 -0
- package/build/angular/components/button-props.d.ts +2 -0
- package/build/angular/components/button-props.js +2 -0
- package/build/angular/components/button-props.js.map +1 -0
- package/build/angular/components/flat-list/index.d.ts +2 -1
- package/build/angular/components/flat-list/index.js +22 -10
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image-background-props.d.ts +6 -0
- package/build/angular/components/image-background-props.js +2 -0
- package/build/angular/components/image-background-props.js.map +1 -0
- package/build/angular/components/{image/shared.d.ts → image-shared.d.ts} +4 -4
- package/build/angular/components/{image/shared.js → image-shared.js} +39 -28
- package/build/angular/components/image-shared.js.map +1 -0
- package/build/angular/components/input-accessory-view-props.d.ts +7 -0
- package/build/angular/components/input-accessory-view-props.js +9 -0
- package/build/angular/components/input-accessory-view-props.js.map +1 -0
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +2 -1
- package/build/angular/components/keyboard-avoiding-view/index.js +28 -27
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +2 -0
- package/build/angular/components/modal/index.js +33 -20
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable-props.d.ts +32 -0
- package/build/angular/components/pressable-props.js +11 -0
- package/build/angular/components/pressable-props.js.map +1 -0
- package/build/angular/components/refresh-control-props.d.ts +16 -0
- package/build/angular/components/refresh-control-props.js +14 -0
- package/build/angular/components/refresh-control-props.js.map +1 -0
- package/build/angular/components/safe-area-view-props.d.ts +7 -0
- package/build/angular/components/safe-area-view-props.js +11 -0
- package/build/angular/components/safe-area-view-props.js.map +1 -0
- package/build/angular/components/scroll-view-props.d.ts +76 -0
- package/build/angular/components/scroll-view-props.js +18 -0
- package/build/angular/components/scroll-view-props.js.map +1 -0
- package/build/angular/components/section-list/index.d.ts +3 -0
- package/build/angular/components/section-list/index.js +10 -1
- package/build/angular/components/section-list/index.js.map +1 -1
- package/build/angular/components/text-input-props.d.ts +45 -0
- package/build/angular/components/text-input-props.js +18 -0
- package/build/angular/components/text-input-props.js.map +1 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +6 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js +2 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js.map +1 -0
- package/build/angular/components/touchable-props.d.ts +14 -0
- package/build/angular/components/touchable-props.js +13 -0
- package/build/angular/components/touchable-props.js.map +1 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +3 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js +2 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js.map +1 -0
- package/build/angular/components/virtualized-list/index.d.ts +8 -1
- package/build/angular/components/virtualized-list/index.js +373 -278
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.d.ts +3 -0
- package/build/angular/components/virtualized-section-list/index.js +10 -1
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/components.d.ts +21 -27
- package/build/angular/components.js +13 -15
- 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 +37 -11
- package/build/angular/create-portal/index.js.map +1 -1
- package/build/angular/create-tunnel/index.js +1 -1
- package/build/angular/element-props.d.ts +53 -0
- package/build/angular/element-props.js +2 -0
- package/build/angular/element-props.js.map +1 -0
- package/build/angular/elements.d.ts +528 -0
- package/build/angular/elements.js +1444 -0
- package/build/angular/elements.js.map +1 -0
- package/build/angular/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/index.d.ts +5 -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 +6 -6
- package/build/angular/modules/animated/create-animated-component.js +73 -81
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/animated/index.js +1 -1
- package/build/angular/modules/animated/index.js.map +1 -1
- package/build/angular/modules/image/index.d.ts +3 -0
- package/build/angular/modules/image/index.js +16 -0
- package/build/angular/modules/image/index.js.map +1 -0
- package/build/angular/primitives/index.d.ts +28 -16
- package/build/angular/primitives/index.js +77 -31
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +2 -3
- package/build/angular/primitives/shared.js +8 -53
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/register.d.ts +1 -0
- package/build/angular/register.js +16 -0
- package/build/angular/register.js.map +1 -0
- package/build/angular/render/index.js +4 -2
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/renderer/index.js +207 -60
- package/build/angular/renderer/index.js.map +1 -1
- package/build/angular/renderer/value-change.d.ts +2 -0
- package/build/angular/renderer/value-change.js +9 -0
- package/build/angular/renderer/value-change.js.map +1 -0
- package/metro-config.cjs +23 -5
- package/metro-css-parser.cjs +2 -1
- package/package.json +17 -7
- package/src/anchor-host-registry.ts +1 -15
- package/src/change-detection-flush.ts +120 -0
- package/src/components/activity-indicator-props.ts +21 -0
- package/src/components/button-props.ts +26 -0
- package/src/components/flat-list/index.ts +12 -4
- package/src/components/image-background-props.ts +24 -0
- package/src/components/{image/shared.ts → image-shared.ts} +57 -26
- package/src/components/input-accessory-view-props.ts +21 -0
- package/src/components/keyboard-avoiding-view/index.ts +27 -16
- package/src/components/modal/index.ts +32 -11
- package/src/components/pressable-props.ts +73 -0
- package/src/components/refresh-control-props.ts +43 -0
- package/src/components/safe-area-view-props.ts +28 -0
- package/src/components/scroll-view-props.ts +109 -0
- package/src/components/section-list/index.ts +18 -0
- package/src/components/text-input-props.ts +72 -0
- package/src/components/touchable-native-feedback/touchable-native-feedback-props.ts +23 -0
- package/src/components/touchable-props.ts +30 -0
- package/src/components/touchable-without-feedback/touchable-without-feedback-props.ts +21 -0
- package/src/components/virtualized-list/index.ts +254 -153
- package/src/components/virtualized-section-list/index.ts +18 -0
- package/src/components.ts +86 -35
- package/src/create-portal/index.ts +39 -9
- package/src/create-tunnel/index.ts +1 -1
- package/src/diagnostics.ts +1 -1
- package/src/element-props.ts +90 -0
- package/src/elements.ts +919 -0
- package/src/gate-demand.ts +109 -0
- package/src/host-instance/index.ts +1 -1
- package/src/index.ts +54 -15
- package/src/modules/animated/create-animated-component.ts +73 -59
- package/src/modules/animated/index.ts +1 -1
- package/src/modules/image/index.ts +18 -0
- package/src/primitives/index.ts +47 -16
- package/src/primitives/shared.ts +11 -44
- package/src/register.ts +15 -0
- package/src/render/index.ts +4 -2
- package/src/renderer/index.ts +239 -80
- package/src/renderer/value-change.ts +8 -0
- package/build/angular/components/activity-indicator/index.android.d.ts +0 -13
- package/build/angular/components/activity-indicator/index.android.js +0 -29
- package/build/angular/components/activity-indicator/index.android.js.map +0 -1
- package/build/angular/components/activity-indicator/index.d.ts +0 -1
- package/build/angular/components/activity-indicator/index.ios.d.ts +0 -9
- package/build/angular/components/activity-indicator/index.ios.js +0 -26
- package/build/angular/components/activity-indicator/index.ios.js.map +0 -1
- package/build/angular/components/activity-indicator/index.js +0 -4
- package/build/angular/components/activity-indicator/index.js.map +0 -1
- package/build/angular/components/activity-indicator/shared.d.ts +0 -67
- package/build/angular/components/activity-indicator/shared.js +0 -260
- package/build/angular/components/activity-indicator/shared.js.map +0 -1
- package/build/angular/components/button.d.ts +0 -65
- package/build/angular/components/button.js +0 -351
- package/build/angular/components/button.js.map +0 -1
- package/build/angular/components/image/index.android.d.ts +0 -15
- package/build/angular/components/image/index.android.js +0 -91
- package/build/angular/components/image/index.android.js.map +0 -1
- package/build/angular/components/image/index.d.ts +0 -1
- package/build/angular/components/image/index.ios.d.ts +0 -15
- package/build/angular/components/image/index.ios.js +0 -91
- package/build/angular/components/image/index.ios.js.map +0 -1
- package/build/angular/components/image/index.js +0 -3
- package/build/angular/components/image/index.js.map +0 -1
- package/build/angular/components/image/shared.js.map +0 -1
- package/build/angular/components/image-background.d.ts +0 -82
- package/build/angular/components/image-background.js +0 -434
- package/build/angular/components/image-background.js.map +0 -1
- package/build/angular/components/input-accessory-view/index.d.ts +0 -65
- package/build/angular/components/input-accessory-view/index.js +0 -290
- package/build/angular/components/input-accessory-view/index.js.map +0 -1
- package/build/angular/components/pressable/index.d.ts +0 -129
- package/build/angular/components/pressable/index.js +0 -473
- package/build/angular/components/pressable/index.js.map +0 -1
- package/build/angular/components/refresh-control/index.d.ts +0 -90
- package/build/angular/components/refresh-control/index.js +0 -380
- package/build/angular/components/refresh-control/index.js.map +0 -1
- package/build/angular/components/safe-area-view/index.d.ts +0 -62
- package/build/angular/components/safe-area-view/index.js +0 -287
- package/build/angular/components/safe-area-view/index.js.map +0 -1
- package/build/angular/components/scroll-view/index.android.d.ts +0 -20
- package/build/angular/components/scroll-view/index.android.js +0 -262
- package/build/angular/components/scroll-view/index.android.js.map +0 -1
- package/build/angular/components/scroll-view/index.d.ts +0 -3
- package/build/angular/components/scroll-view/index.ios.d.ts +0 -8
- package/build/angular/components/scroll-view/index.ios.js +0 -92
- package/build/angular/components/scroll-view/index.ios.js.map +0 -1
- package/build/angular/components/scroll-view/index.js +0 -6
- package/build/angular/components/scroll-view/index.js.map +0 -1
- package/build/angular/components/scroll-view/projection.d.ts +0 -67
- package/build/angular/components/scroll-view/projection.js +0 -560
- package/build/angular/components/scroll-view/projection.js.map +0 -1
- package/build/angular/components/scroll-view/shared.d.ts +0 -239
- package/build/angular/components/scroll-view/shared.js +0 -832
- package/build/angular/components/scroll-view/shared.js.map +0 -1
- package/build/angular/components/scroll-view/sticky-header.d.ts +0 -40
- package/build/angular/components/scroll-view/sticky-header.js +0 -224
- package/build/angular/components/scroll-view/sticky-header.js.map +0 -1
- package/build/angular/components/switch/index.android.d.ts +0 -24
- package/build/angular/components/switch/index.android.js +0 -112
- package/build/angular/components/switch/index.android.js.map +0 -1
- package/build/angular/components/switch/index.d.ts +0 -1
- package/build/angular/components/switch/index.ios.d.ts +0 -23
- package/build/angular/components/switch/index.ios.js +0 -111
- package/build/angular/components/switch/index.ios.js.map +0 -1
- package/build/angular/components/switch/index.js +0 -4
- package/build/angular/components/switch/index.js.map +0 -1
- package/build/angular/components/switch/shared.d.ts +0 -49
- package/build/angular/components/switch/shared.js +0 -191
- package/build/angular/components/switch/shared.js.map +0 -1
- package/build/angular/components/text-input.d.ts +0 -161
- package/build/angular/components/text-input.js +0 -659
- package/build/angular/components/text-input.js.map +0 -1
- package/build/angular/components/touchable/index.d.ts +0 -276
- package/build/angular/components/touchable/index.js +0 -1422
- package/build/angular/components/touchable/index.js.map +0 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +0 -86
- package/build/angular/components/touchable-native-feedback/index.js +0 -407
- package/build/angular/components/touchable-native-feedback/index.js.map +0 -1
- package/src/components/activity-indicator/index.android.ts +0 -23
- package/src/components/activity-indicator/index.ios.ts +0 -21
- package/src/components/activity-indicator/index.ts +0 -4
- package/src/components/activity-indicator/shared.ts +0 -225
- package/src/components/button.ts +0 -220
- package/src/components/image/index.android.ts +0 -99
- package/src/components/image/index.ios.ts +0 -99
- package/src/components/image/index.ts +0 -3
- package/src/components/image-background.ts +0 -290
- package/src/components/input-accessory-view/index.ts +0 -240
- package/src/components/pressable/index.ts +0 -462
- package/src/components/refresh-control/index.ts +0 -340
- package/src/components/safe-area-view/index.ts +0 -231
- package/src/components/scroll-view/index.android.ts +0 -205
- package/src/components/scroll-view/index.ios.ts +0 -81
- package/src/components/scroll-view/index.ts +0 -7
- package/src/components/scroll-view/projection.ts +0 -713
- package/src/components/scroll-view/shared.ts +0 -1066
- package/src/components/scroll-view/sticky-header.ts +0 -267
- package/src/components/switch/index.android.ts +0 -113
- package/src/components/switch/index.ios.ts +0 -112
- package/src/components/switch/index.ts +0 -4
- package/src/components/switch/shared.ts +0 -284
- package/src/components/text-input.ts +0 -613
- package/src/components/touchable/index.ts +0 -913
- package/src/components/touchable-native-feedback/index.ts +0 -317
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// in ngAfterViewChecked. OnPush + ChangeDetectorRef.markForCheck() drives re-render off the native
|
|
10
10
|
// scroll/layout/measure callbacks (they fire outside Angular's own event bindings, so they must
|
|
11
11
|
// mark the view dirty). This is the Angular twin of the React useReducer/useEffect and Vue
|
|
12
|
-
// ref/computed/watch over the same shared functions. It
|
|
13
|
-
// as the React/Vue lists drive their
|
|
12
|
+
// ref/computed/watch over the same shared functions. It writes the `scroll-view`/
|
|
13
|
+
// `horizontal-scroll-view` TAG directly, exactly as the React/Vue lists drive their scroll tag.
|
|
14
14
|
//
|
|
15
15
|
// Per-item rendering is TEMPLATES, not a callback: React/Vue `renderItem: (info) => element` does
|
|
16
16
|
// not translate to Angular. The app supplies the cell via `<ng-template vListItem>` (and the
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
// shape differs, since a callback prop returning an element has no Angular equivalent.
|
|
23
23
|
//
|
|
24
24
|
// Lists have no Descriptor render fn — the cell content is the framework's own children.
|
|
25
|
-
// Cells/spacers are plain
|
|
25
|
+
// Cells/spacers are plain view host elements.
|
|
26
26
|
|
|
27
27
|
import {
|
|
28
28
|
CUSTOM_ELEMENTS_SCHEMA,
|
|
@@ -80,20 +80,21 @@ import {
|
|
|
80
80
|
type IViewabilityConfigCallbackPair,
|
|
81
81
|
type IViewableItemsChangedInfo,
|
|
82
82
|
type IVirtualizedListHandle,
|
|
83
|
+
buildScrollViewHandle,
|
|
83
84
|
} from '@symbiote-native/components';
|
|
84
85
|
import {
|
|
85
86
|
dlog,
|
|
86
87
|
flattenStyle,
|
|
88
|
+
isSymbioteNode,
|
|
87
89
|
type IStyleProp,
|
|
88
90
|
type ISymbioteEvent,
|
|
89
91
|
type ISymbioteNode,
|
|
90
92
|
type IViewStyle,
|
|
91
93
|
} from '@symbiote-native/engine';
|
|
92
94
|
import { countAngular } from '../../diagnostics';
|
|
93
|
-
import { ScrollView } from '../scroll-view';
|
|
94
|
-
import { RefreshControl } from '../refresh-control';
|
|
95
95
|
import {
|
|
96
96
|
anchorHostStyle,
|
|
97
|
+
SymbioteHostPropsDirective,
|
|
97
98
|
SymbioteStyleInputDirective,
|
|
98
99
|
ViewHost,
|
|
99
100
|
} from '../../primitives';
|
|
@@ -127,6 +128,11 @@ export {
|
|
|
127
128
|
VListItemDirective,
|
|
128
129
|
VListSeparatorDirective,
|
|
129
130
|
} from './directives';
|
|
131
|
+
import {
|
|
132
|
+
gateWanted,
|
|
133
|
+
injectGateDemandAbove,
|
|
134
|
+
type IGatedAccessibilityEvent,
|
|
135
|
+
} from '../../gate-demand';
|
|
130
136
|
export type { IVListItemContext, IVListSeparatorContext } from './directives';
|
|
131
137
|
|
|
132
138
|
// The Angular VirtualizedList prop surface. Mirrors React/Vue's IVirtualizedListProps MINUS the
|
|
@@ -210,6 +216,12 @@ interface IWindowCell<ItemT> {
|
|
|
210
216
|
separatorContext?: IVListSeparatorContext<ItemT>;
|
|
211
217
|
}
|
|
212
218
|
|
|
219
|
+
// The scroll body is identical on both axes; only the outer tag differs (the axis comes from the
|
|
220
|
+
// TAG, never a prop — `<horizontal-scroll-view>` vs `<scroll-view>`, matching every other adapter,
|
|
221
|
+
// `.claude/rules/host-primitive-tier.md`). No shared-outlet primitive exists to de-duplicate this
|
|
222
|
+
// (this package's own NgTemplateOutlet twin is keyed on a per-cell context, not a static block, and
|
|
223
|
+
// ngc's partial evaluator needs the decorator's `template` to be one literal), so the body is
|
|
224
|
+
// duplicated across the two branches — the same shape Svelte's own `{#if horizontal}` takes.
|
|
213
225
|
@Component({
|
|
214
226
|
selector: 'VirtualizedList',
|
|
215
227
|
standalone: true,
|
|
@@ -217,137 +229,149 @@ interface IWindowCell<ItemT> {
|
|
|
217
229
|
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
218
230
|
],
|
|
219
231
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
220
|
-
imports: [
|
|
232
|
+
imports: [SymbioteHostPropsDirective, VListOutletDirective, ViewHost],
|
|
221
233
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
222
234
|
template: `
|
|
223
|
-
|
|
224
|
-
[
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
[accessibilityLabel]="foldedAccessibility().accessibilityLabel"
|
|
231
|
-
[accessibilityHint]="foldedAccessibility().accessibilityHint"
|
|
232
|
-
[accessibilityRole]="foldedAccessibility().accessibilityRole"
|
|
233
|
-
[accessibilityState]="foldedAccessibility().accessibilityState"
|
|
234
|
-
[accessibilityValue]="foldedAccessibility().accessibilityValue"
|
|
235
|
-
[accessibilityActions]="foldedAccessibility().accessibilityActions"
|
|
236
|
-
[accessibilityLabelledBy]="foldedAccessibility().accessibilityLabelledBy"
|
|
237
|
-
[importantForAccessibility]="
|
|
238
|
-
foldedAccessibility().importantForAccessibility
|
|
239
|
-
"
|
|
240
|
-
[accessibilityLiveRegion]="foldedAccessibility().accessibilityLiveRegion"
|
|
241
|
-
[screenReaderFocusable]="foldedAccessibility().screenReaderFocusable"
|
|
242
|
-
[accessibilityViewIsModal]="
|
|
243
|
-
foldedAccessibility().accessibilityViewIsModal
|
|
244
|
-
"
|
|
245
|
-
[accessibilityElementsHidden]="
|
|
246
|
-
foldedAccessibility().accessibilityElementsHidden
|
|
247
|
-
"
|
|
248
|
-
[accessibilityIgnoresInvertColors]="
|
|
249
|
-
foldedAccessibility().accessibilityIgnoresInvertColors
|
|
250
|
-
"
|
|
251
|
-
[accessibilityLanguage]="foldedAccessibility().accessibilityLanguage"
|
|
252
|
-
[accessibilityRespondsToUserInteraction]="
|
|
253
|
-
foldedAccessibility().accessibilityRespondsToUserInteraction
|
|
254
|
-
"
|
|
255
|
-
[accessibilityShowsLargeContentViewer]="
|
|
256
|
-
foldedAccessibility().accessibilityShowsLargeContentViewer
|
|
257
|
-
"
|
|
258
|
-
[accessibilityLargeContentTitle]="
|
|
259
|
-
foldedAccessibility().accessibilityLargeContentTitle
|
|
260
|
-
"
|
|
261
|
-
(accessibilityAction)="accessibilityActionTick($event)"
|
|
262
|
-
(accessibilityTap)="accessibilityTapTick($event)"
|
|
263
|
-
(magicTap)="magicTapTick($event)"
|
|
264
|
-
(accessibilityEscape)="accessibilityEscapeTick($event)"
|
|
265
|
-
[onScroll]="onScrollTick"
|
|
266
|
-
(layout)="onLayoutTick($event)"
|
|
267
|
-
[onScrollBeginDrag]="onScrollBeginDrag"
|
|
268
|
-
[onScrollEndDrag]="onScrollEndDrag"
|
|
269
|
-
[onMomentumScrollBegin]="onMomentumScrollBegin"
|
|
270
|
-
[onMomentumScrollEnd]="onMomentumScrollEnd"
|
|
271
|
-
[scrollEventThrottle]="scrollEventThrottle"
|
|
272
|
-
[keyboardShouldPersistTaps]="keyboardShouldPersistTaps"
|
|
273
|
-
[keyboardDismissMode]="keyboardDismissMode"
|
|
274
|
-
[contentOffset]="commandedOffset"
|
|
275
|
-
[stickyHeaderIndices]="renderedStickyIndices"
|
|
276
|
-
[maintainVisibleContentPosition]="resolvedMaintainVisibleContentPosition"
|
|
277
|
-
>
|
|
278
|
-
@if (shouldRenderRefreshControl) {
|
|
279
|
-
<RefreshControl
|
|
280
|
-
[refreshing]="refreshing ?? false"
|
|
281
|
-
(refresh)="handleRefresh()"
|
|
282
|
-
[progressViewOffset]="progressViewOffset"
|
|
283
|
-
/>
|
|
284
|
-
}
|
|
235
|
+
@if (isHorizontal) {
|
|
236
|
+
<horizontal-scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
237
|
+
@if (shouldRenderRefreshControl) {
|
|
238
|
+
<refresh-control
|
|
239
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
240
|
+
></refresh-control>
|
|
241
|
+
}
|
|
285
242
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
243
|
+
@if (headerDir !== undefined) {
|
|
244
|
+
<view>
|
|
245
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
246
|
+
</view>
|
|
247
|
+
}
|
|
291
248
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
249
|
+
@if (itemCount === 0) {
|
|
250
|
+
@if (emptyDir !== undefined) {
|
|
251
|
+
<view>
|
|
252
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
253
|
+
</view>
|
|
254
|
+
}
|
|
255
|
+
} @else {
|
|
256
|
+
@if (leadingSpacerStyle !== null) {
|
|
257
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
258
|
+
}
|
|
259
|
+
@if (forcedStickyCell !== null) {
|
|
260
|
+
<view
|
|
261
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
262
|
+
[style]="cellStyle"
|
|
263
|
+
>
|
|
264
|
+
<ng-container
|
|
265
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
266
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
267
|
+
></ng-container>
|
|
268
|
+
</view>
|
|
269
|
+
}
|
|
270
|
+
@if (gapSpacerStyle !== null) {
|
|
271
|
+
<view [style]="gapSpacerStyle"></view>
|
|
272
|
+
}
|
|
273
|
+
@for (cell of windowCells; track cell.key) {
|
|
274
|
+
<view
|
|
275
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
276
|
+
[style]="cellStyle"
|
|
277
|
+
>
|
|
278
|
+
<ng-container
|
|
279
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
280
|
+
[vListOutletContext]="cell.context"
|
|
281
|
+
></ng-container>
|
|
282
|
+
@if (cell.separatorContext !== undefined) {
|
|
283
|
+
<ng-container
|
|
284
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
285
|
+
[vListOutletContext]="cell.separatorContext"
|
|
286
|
+
></ng-container>
|
|
287
|
+
}
|
|
288
|
+
</view>
|
|
289
|
+
}
|
|
290
|
+
@if (trailingSpacerStyle !== null) {
|
|
291
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
292
|
+
}
|
|
297
293
|
}
|
|
298
|
-
|
|
299
|
-
@if (
|
|
300
|
-
<
|
|
294
|
+
|
|
295
|
+
@if (footerDir !== undefined) {
|
|
296
|
+
<view>
|
|
297
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
298
|
+
</view>
|
|
301
299
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
[vListOutletContext]="forcedStickyCell.context"
|
|
310
|
-
></ng-container>
|
|
311
|
-
</symbiote-view>
|
|
300
|
+
</horizontal-scroll-view>
|
|
301
|
+
} @else {
|
|
302
|
+
<scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
303
|
+
@if (shouldRenderRefreshControl) {
|
|
304
|
+
<refresh-control
|
|
305
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
306
|
+
></refresh-control>
|
|
312
307
|
}
|
|
313
|
-
|
|
314
|
-
|
|
308
|
+
|
|
309
|
+
@if (headerDir !== undefined) {
|
|
310
|
+
<view>
|
|
311
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
312
|
+
</view>
|
|
315
313
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
@
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
<
|
|
329
|
-
|
|
330
|
-
[
|
|
331
|
-
|
|
332
|
-
@if (cell.separatorContext !== undefined) {
|
|
314
|
+
|
|
315
|
+
@if (itemCount === 0) {
|
|
316
|
+
@if (emptyDir !== undefined) {
|
|
317
|
+
<view>
|
|
318
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
319
|
+
</view>
|
|
320
|
+
}
|
|
321
|
+
} @else {
|
|
322
|
+
@if (leadingSpacerStyle !== null) {
|
|
323
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
324
|
+
}
|
|
325
|
+
@if (forcedStickyCell !== null) {
|
|
326
|
+
<view
|
|
327
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
328
|
+
[style]="cellStyle"
|
|
329
|
+
>
|
|
333
330
|
<ng-container
|
|
334
|
-
[vListOutlet]="
|
|
335
|
-
[vListOutletContext]="
|
|
331
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
332
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
336
333
|
></ng-container>
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
334
|
+
</view>
|
|
335
|
+
}
|
|
336
|
+
@if (gapSpacerStyle !== null) {
|
|
337
|
+
<view [style]="gapSpacerStyle"></view>
|
|
338
|
+
}
|
|
339
|
+
<!-- The separator sits INSIDE the measuring view, as RN's own cell renderer places it
|
|
340
|
+
(VirtualizedListCellRenderer.js:218-221). As a sibling it is an extra flex child, so
|
|
341
|
+
the chrome between two cells is gap + separator + gap while a spacer collapsing that
|
|
342
|
+
region replaces it with a single gap — every cell below the leading spacer then lands
|
|
343
|
+
short by (separator + gap) and the content jumps by that much whenever the window's
|
|
344
|
+
first index moves. Measured at exactly 17px on device 2026-08-19; see
|
|
345
|
+
.claude/rules/list-geometry-feedback-loop.md. -->
|
|
346
|
+
@for (cell of windowCells; track cell.key) {
|
|
347
|
+
<view
|
|
348
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
349
|
+
[style]="cellStyle"
|
|
350
|
+
>
|
|
351
|
+
<ng-container
|
|
352
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
353
|
+
[vListOutletContext]="cell.context"
|
|
354
|
+
></ng-container>
|
|
355
|
+
@if (cell.separatorContext !== undefined) {
|
|
356
|
+
<ng-container
|
|
357
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
358
|
+
[vListOutletContext]="cell.separatorContext"
|
|
359
|
+
></ng-container>
|
|
360
|
+
}
|
|
361
|
+
</view>
|
|
362
|
+
}
|
|
363
|
+
@if (trailingSpacerStyle !== null) {
|
|
364
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
365
|
+
}
|
|
342
366
|
}
|
|
343
|
-
}
|
|
344
367
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
368
|
+
@if (footerDir !== undefined) {
|
|
369
|
+
<view>
|
|
370
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
371
|
+
</view>
|
|
372
|
+
}
|
|
373
|
+
</scroll-view>
|
|
374
|
+
}
|
|
351
375
|
`,
|
|
352
376
|
})
|
|
353
377
|
export class VirtualizedList<ItemT = unknown>
|
|
@@ -361,7 +385,7 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
361
385
|
{
|
|
362
386
|
// The list's edge/viewability/failure events as real Angular events: `(endReached)="…"`, not
|
|
363
387
|
// `[onEndReached]="…"`. See handleRefresh/accessibility*Tick below for how the still-callback-
|
|
364
|
-
// shaped
|
|
388
|
+
// shaped scroll-view/refresh-control bag keys are fed from these.
|
|
365
389
|
@Output() readonly endReached = new EventEmitter<{
|
|
366
390
|
distanceFromEnd: number;
|
|
367
391
|
}>();
|
|
@@ -381,6 +405,16 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
381
405
|
@Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
|
|
382
406
|
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
383
407
|
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
408
|
+
|
|
409
|
+
// This wrapper binds the four gated accessibility events on the component it renders, which
|
|
410
|
+
// Angular forces to be unconditional and which would light that component's gates on every
|
|
411
|
+
// instance. It answers for them instead — see `gate-demand.ts`.
|
|
412
|
+
private readonly gateDemandAbove = injectGateDemandAbove();
|
|
413
|
+
|
|
414
|
+
wantsGate(name: IGatedAccessibilityEvent): boolean {
|
|
415
|
+
return gateWanted(this.gateDemandAbove, name, this[name]);
|
|
416
|
+
}
|
|
417
|
+
|
|
384
418
|
@Input({ required: true }) data!: unknown;
|
|
385
419
|
@Input({ required: true }) getItem!: (data: unknown, index: number) => ItemT;
|
|
386
420
|
@Input({ required: true }) getItemCount!: (data: unknown) => number;
|
|
@@ -475,10 +509,24 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
475
509
|
@ContentChild(VListSeparatorDirective)
|
|
476
510
|
separatorDir?: VListSeparatorDirective<ItemT>;
|
|
477
511
|
|
|
478
|
-
// The
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
|
|
512
|
+
// The inner scroll TAG's own engine node — a template ref on a bare intrinsic hands back the
|
|
513
|
+
// host node directly (`isSymbioteNode(elementRef.nativeElement)`, matching `anchorHostStyle`'s
|
|
514
|
+
// own reader below), so there is no wrapper component left to read a handle off. Present from
|
|
515
|
+
// whichever branch (`scroll-view` / `horizontal-scroll-view`) is currently mounted, once the
|
|
516
|
+
// template has run at least one CD pass.
|
|
517
|
+
@ViewChild('scrollHost', { read: ElementRef })
|
|
518
|
+
private scrollHostRef?: ElementRef<unknown>;
|
|
519
|
+
|
|
520
|
+
private get scrollNode(): ISymbioteNode | null {
|
|
521
|
+
const node = this.scrollHostRef?.nativeElement;
|
|
522
|
+
return isSymbioteNode(node) ? node : null;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// `buildScrollViewHandle` reads the node lazily through the getter above and no-ops before
|
|
526
|
+
// commit — the same function every other adapter feeds from its own host-instance accessor.
|
|
527
|
+
private readonly scrollHandle: IScrollViewHandle = buildScrollViewHandle(
|
|
528
|
+
() => this.scrollNode,
|
|
529
|
+
);
|
|
482
530
|
|
|
483
531
|
// --- template-bound view state, assembled in ngDoCheck (recomputeView) ---
|
|
484
532
|
itemCount = EMPTY_OFFSET;
|
|
@@ -560,13 +608,13 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
560
608
|
|
|
561
609
|
private readonly cdr = inject(ChangeDetectorRef);
|
|
562
610
|
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
563
|
-
// onto (see anchorHostStyle's doc comment) — NOT `
|
|
564
|
-
// inner `<
|
|
611
|
+
// onto (see anchorHostStyle's doc comment) — NOT `scrollHostRef` above, which targets the real
|
|
612
|
+
// inner `<scroll-view>`/`<horizontal-scroll-view>` tag one level down.
|
|
565
613
|
private readonly elementRef = inject(ElementRef);
|
|
566
614
|
|
|
567
|
-
// Bridges the non-reactive @Input fields into the reactive graph so
|
|
568
|
-
// memoize a bag derived from them (
|
|
569
|
-
//
|
|
615
|
+
// Bridges the non-reactive @Input fields into the reactive graph so `foldedAccessibility` below
|
|
616
|
+
// can memoize a bag derived from them (read its own comment for why signal inputs are not an
|
|
617
|
+
// option while this package's unit suite runs on JIT).
|
|
570
618
|
//
|
|
571
619
|
// ONLY safe for a bag whose every dependency is an @Input. This list's window machinery
|
|
572
620
|
// (listState, renderVersion, windowCells, the measured metrics) is driven from scroll/layout
|
|
@@ -575,12 +623,13 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
575
623
|
// instead of widening this one.
|
|
576
624
|
private readonly inputsRevision = signal(0);
|
|
577
625
|
|
|
578
|
-
// MEASURED: the template
|
|
579
|
-
// not cache a getter across binding expressions - as a getter it
|
|
580
|
-
// refresh, and every consumer saw a fresh reference, so all 20
|
|
581
|
-
//
|
|
582
|
-
// input actually changes. Every dependency below is an @Input, which is what makes
|
|
583
|
-
// inputsRevision a complete dependency set here
|
|
626
|
+
// MEASURED (while ScrollView was still a wrapper): the template fed 20 separate inputs off this
|
|
627
|
+
// one bag, and Angular does not cache a getter across binding expressions - as a getter it
|
|
628
|
+
// rebuilt the object 20 times per refresh, and every consumer saw a fresh reference, so all 20
|
|
629
|
+
// reported "changed" and wrote through. A computed evaluates once and hands back the SAME object
|
|
630
|
+
// until an input actually changes. Every dependency below is an @Input, which is what makes
|
|
631
|
+
// inputsRevision a complete dependency set here — `scrollViewBag()` reuses this result rather
|
|
632
|
+
// than re-running the fold.
|
|
584
633
|
readonly foldedAccessibility = computed<
|
|
585
634
|
IAccessibilityProps & IAriaProps & Record<string, unknown>
|
|
586
635
|
>(() => {
|
|
@@ -630,6 +679,57 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
630
679
|
return this.refreshRequested ?? this.refresh.observed;
|
|
631
680
|
}
|
|
632
681
|
|
|
682
|
+
// The flat bag fed to the scroll tag via `[symbioteHostProps]`. A plain method, not `computed()`:
|
|
683
|
+
// most of these fields (resolvedStyle, renderedStickyIndices, commandedOffset, …) are recomputed
|
|
684
|
+
// in ngDoCheck/dispatch as ordinary fields, not signals, so a computed() memoized only against
|
|
685
|
+
// `inputsRevision` would go stale the moment one of them changed outside an @Input() write. The
|
|
686
|
+
// directive itself diffs per key before writing to the engine (`primitives/shared.ts`), so a
|
|
687
|
+
// fresh object every CD pass costs nothing beyond the literal allocation.
|
|
688
|
+
scrollViewBag(): Record<string, unknown> {
|
|
689
|
+
return {
|
|
690
|
+
...this.foldedAccessibility(),
|
|
691
|
+
style: this.resolvedStyle,
|
|
692
|
+
contentContainerStyle: this.resolvedContentContainerStyle,
|
|
693
|
+
onScroll: this.onScrollTick,
|
|
694
|
+
onLayout: this.onLayoutTick,
|
|
695
|
+
onScrollBeginDrag: this.onScrollBeginDrag,
|
|
696
|
+
onScrollEndDrag: this.onScrollEndDrag,
|
|
697
|
+
onMomentumScrollBegin: this.onMomentumScrollBegin,
|
|
698
|
+
onMomentumScrollEnd: this.onMomentumScrollEnd,
|
|
699
|
+
scrollEventThrottle: this.scrollEventThrottle,
|
|
700
|
+
keyboardShouldPersistTaps: this.keyboardShouldPersistTaps,
|
|
701
|
+
keyboardDismissMode: this.keyboardDismissMode,
|
|
702
|
+
contentOffset: this.commandedOffset,
|
|
703
|
+
stickyHeaderIndices: this.renderedStickyIndices,
|
|
704
|
+
maintainVisibleContentPosition:
|
|
705
|
+
this.resolvedMaintainVisibleContentPosition,
|
|
706
|
+
// The four boolean-gated Fabric events (`.claude/rules/fabric-boolean-event-gates.md`): the
|
|
707
|
+
// TAG has no `.observed` of its own, so `wantsGate` (own subscriber, or the demand from
|
|
708
|
+
// whatever wraps this list) is what decides whether the flag reaches Fabric at all.
|
|
709
|
+
onAccessibilityAction: this.wantsGate('accessibilityAction')
|
|
710
|
+
? this.accessibilityActionTick
|
|
711
|
+
: undefined,
|
|
712
|
+
onAccessibilityTap: this.wantsGate('accessibilityTap')
|
|
713
|
+
? this.accessibilityTapTick
|
|
714
|
+
: undefined,
|
|
715
|
+
onMagicTap: this.wantsGate('magicTap') ? this.magicTapTick : undefined,
|
|
716
|
+
onAccessibilityEscape: this.wantsGate('accessibilityEscape')
|
|
717
|
+
? this.accessibilityEscapeTick
|
|
718
|
+
: undefined,
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// The `refresh-control` tag's own bag — an ordinary first child, re-parented per platform by the
|
|
723
|
+
// scroll-view behavior (`../refresh-control-props.ts`). `onRefresh` is a plain listener prop, not
|
|
724
|
+
// a gated one: RefreshControl's `refresh` is unaffected by `fabric-boolean-event-gates.md`.
|
|
725
|
+
refreshControlBag(): Record<string, unknown> {
|
|
726
|
+
return {
|
|
727
|
+
refreshing: this.refreshing ?? false,
|
|
728
|
+
progressViewOffset: this.progressViewOffset,
|
|
729
|
+
onRefresh: this.handleRefresh,
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
|
|
633
733
|
get isHorizontal(): boolean {
|
|
634
734
|
return this.horizontal === true;
|
|
635
735
|
}
|
|
@@ -657,9 +757,10 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
657
757
|
return this.onStartReachedThreshold ?? DEFAULT_START_REACHED_THRESHOLD;
|
|
658
758
|
}
|
|
659
759
|
|
|
660
|
-
//
|
|
661
|
-
//
|
|
662
|
-
//
|
|
760
|
+
// onScroll stays a plain callback bag key (an Animated.event(...) target must be able to flow
|
|
761
|
+
// through it, which an @Output() can't carry), so this is a stable arrow field read straight into
|
|
762
|
+
// scrollViewBag()'s `onScroll`. onLayout is unconditional too — this list always needs its own
|
|
763
|
+
// layout for windowing, regardless of whether an app subscribes to anything.
|
|
663
764
|
onScrollTick = (event: ISymbioteEvent): void => {
|
|
664
765
|
countAngular('scrollTicks');
|
|
665
766
|
const offset = readScrollOffset(event, this.isHorizontal);
|
|
@@ -679,9 +780,9 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
679
780
|
this.dispatch({ kind: 'layout', length });
|
|
680
781
|
};
|
|
681
782
|
|
|
682
|
-
// RefreshControl's refresh and
|
|
683
|
-
//
|
|
684
|
-
//
|
|
783
|
+
// RefreshControl's refresh and the scroll tag's accessibility events reach here as plain
|
|
784
|
+
// listener-prop values in scrollViewBag()/refreshControlBag() — these fields just adapt
|
|
785
|
+
// VirtualizedList's own @Output() into a plain re-emit callback.
|
|
685
786
|
handleRefresh = (): void => {
|
|
686
787
|
this.refresh.emit();
|
|
687
788
|
};
|
|
@@ -1245,23 +1346,23 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
1245
1346
|
}
|
|
1246
1347
|
|
|
1247
1348
|
flashScrollIndicators(): void {
|
|
1248
|
-
this.
|
|
1349
|
+
this.scrollHandle.flashScrollIndicators();
|
|
1249
1350
|
}
|
|
1250
1351
|
|
|
1251
1352
|
getNativeScrollRef(): IScrollViewHandle | null {
|
|
1252
|
-
return this.
|
|
1353
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
1253
1354
|
}
|
|
1254
1355
|
|
|
1255
1356
|
getScrollableNode(): IScrollViewHandle | null {
|
|
1256
|
-
return this.
|
|
1357
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
1257
1358
|
}
|
|
1258
1359
|
|
|
1259
1360
|
getScrollResponder(): IScrollViewHandle | null {
|
|
1260
|
-
return this.
|
|
1361
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
1261
1362
|
}
|
|
1262
1363
|
|
|
1263
1364
|
getScrollNode(): ISymbioteNode | null {
|
|
1264
|
-
return this.
|
|
1365
|
+
return this.scrollHandle.getScrollNode();
|
|
1265
1366
|
}
|
|
1266
1367
|
|
|
1267
1368
|
recordInteraction(): void {
|
|
@@ -1273,11 +1374,11 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
1273
1374
|
const target = this.isHorizontal
|
|
1274
1375
|
? { x: clamped, y: EMPTY_OFFSET }
|
|
1275
1376
|
: { x: EMPTY_OFFSET, y: clamped };
|
|
1276
|
-
if (this.
|
|
1377
|
+
if (this.scrollNode !== null) {
|
|
1277
1378
|
dlog(
|
|
1278
1379
|
`Angular VirtualizedList scrollTo offset=${clamped} animated=${animated} (horizontal=${this.isHorizontal})`,
|
|
1279
1380
|
);
|
|
1280
|
-
this.
|
|
1381
|
+
this.scrollHandle.scrollTo({ x: target.x, y: target.y, animated });
|
|
1281
1382
|
return;
|
|
1282
1383
|
}
|
|
1283
1384
|
dlog(`Angular VirtualizedList scrollTo offset=${clamped} pending-ref`);
|
|
@@ -88,6 +88,13 @@ export {
|
|
|
88
88
|
VSectionItemDirective,
|
|
89
89
|
VSectionSeparatorDirective,
|
|
90
90
|
} from './directives';
|
|
91
|
+
import {
|
|
92
|
+
gateWanted,
|
|
93
|
+
injectGateDemandAbove,
|
|
94
|
+
provideGateDemand,
|
|
95
|
+
type IGateDemand,
|
|
96
|
+
type IGatedAccessibilityEvent,
|
|
97
|
+
} from '../../gate-demand';
|
|
91
98
|
export type { IVSectionContext, IVSectionItemContext } from './directives';
|
|
92
99
|
|
|
93
100
|
// The Angular VirtualizedSectionList prop surface. Mirrors React/Vue's IVirtualizedSectionListProps
|
|
@@ -160,6 +167,7 @@ export type IVirtualizedSectionListInputs<ItemT> = Omit<
|
|
|
160
167
|
@Component({
|
|
161
168
|
selector: 'VirtualizedSectionList',
|
|
162
169
|
standalone: true,
|
|
170
|
+
viewProviders: [provideGateDemand(() => VirtualizedSectionList)],
|
|
163
171
|
hostDirectives: [
|
|
164
172
|
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
165
173
|
],
|
|
@@ -416,6 +424,16 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
416
424
|
@Output() readonly accessibilityTap = new EventEmitter<ISymbioteEvent>();
|
|
417
425
|
@Output() readonly magicTap = new EventEmitter<ISymbioteEvent>();
|
|
418
426
|
@Output() readonly accessibilityEscape = new EventEmitter<ISymbioteEvent>();
|
|
427
|
+
|
|
428
|
+
// This wrapper binds the four gated accessibility events on the component it renders, which
|
|
429
|
+
// Angular forces to be unconditional and which would light that component's gates on every
|
|
430
|
+
// instance. It answers for them instead — see `gate-demand.ts`.
|
|
431
|
+
private readonly gateDemandAbove = injectGateDemandAbove();
|
|
432
|
+
|
|
433
|
+
wantsGate(name: IGatedAccessibilityEvent): boolean {
|
|
434
|
+
return gateWanted(this.gateDemandAbove, name, this[name]);
|
|
435
|
+
}
|
|
436
|
+
|
|
419
437
|
@Input() role?: IAriaProps['role'];
|
|
420
438
|
@Input() ariaLabel?: string;
|
|
421
439
|
@Input() ariaLabelledBy?: string;
|