@symbiote-native/angular 1.0.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 +41 -19
- package/bin/symbiote-angular-dev.cjs +16 -4
- package/build/angular/anchor-host-registry.js +1 -17
- package/build/angular/anchor-host-registry.js.map +1 -1
- package/build/angular/change-detection-flush.d.ts +31 -0
- package/build/angular/change-detection-flush.js +110 -0
- package/build/angular/change-detection-flush.js.map +1 -0
- package/build/angular/components/activity-indicator-props.d.ts +2 -0
- package/build/angular/components/activity-indicator-props.js +2 -0
- package/build/angular/components/activity-indicator-props.js.map +1 -0
- package/build/angular/components/button-props.d.ts +2 -0
- package/build/angular/components/button-props.js +2 -0
- package/build/angular/components/button-props.js.map +1 -0
- package/build/angular/components/flat-list/index.d.ts +2 -1
- package/build/angular/components/flat-list/index.js +22 -10
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image-background-props.d.ts +6 -0
- package/build/angular/components/image-background-props.js +2 -0
- package/build/angular/components/image-background-props.js.map +1 -0
- package/build/angular/components/{image/shared.d.ts → image-shared.d.ts} +1 -1
- package/build/angular/components/{image/shared.js → image-shared.js} +7 -2
- package/build/angular/components/image-shared.js.map +1 -0
- package/build/angular/components/input-accessory-view-props.d.ts +7 -0
- package/build/angular/components/input-accessory-view-props.js +9 -0
- package/build/angular/components/input-accessory-view-props.js.map +1 -0
- package/build/angular/components/keyboard-avoiding-view/index.js +11 -17
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +1 -0
- package/build/angular/components/modal/index.js +19 -12
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable-props.d.ts +32 -0
- package/build/angular/components/pressable-props.js +11 -0
- package/build/angular/components/pressable-props.js.map +1 -0
- package/build/angular/components/refresh-control-props.d.ts +16 -0
- package/build/angular/components/refresh-control-props.js +14 -0
- package/build/angular/components/refresh-control-props.js.map +1 -0
- package/build/angular/components/safe-area-view-props.d.ts +7 -0
- package/build/angular/components/safe-area-view-props.js +11 -0
- package/build/angular/components/safe-area-view-props.js.map +1 -0
- package/build/angular/components/scroll-view-props.d.ts +76 -0
- package/build/angular/components/scroll-view-props.js +18 -0
- package/build/angular/components/scroll-view-props.js.map +1 -0
- package/build/angular/components/text-input-props.d.ts +45 -0
- package/build/angular/components/text-input-props.js +18 -0
- package/build/angular/components/text-input-props.js.map +1 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +6 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js +2 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js.map +1 -0
- package/build/angular/components/touchable-props.d.ts +14 -0
- package/build/angular/components/touchable-props.js +13 -0
- package/build/angular/components/touchable-props.js.map +1 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +3 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js +2 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js.map +1 -0
- package/build/angular/components/virtualized-list/index.d.ts +5 -1
- package/build/angular/components/virtualized-list/index.js +366 -280
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components.d.ts +20 -26
- package/build/angular/components.js +12 -14
- package/build/angular/components.js.map +1 -1
- package/build/angular/create-portal/index.d.ts +2 -2
- package/build/angular/create-portal/index.js +5 -5
- package/build/angular/create-tunnel/index.js +1 -1
- package/build/angular/element-props.d.ts +53 -0
- package/build/angular/element-props.js +2 -0
- package/build/angular/element-props.js.map +1 -0
- package/build/angular/elements.d.ts +528 -0
- package/build/angular/elements.js +1444 -0
- package/build/angular/elements.js.map +1 -0
- package/build/angular/host-instance/index.js +1 -1
- package/build/angular/index.d.ts +4 -2
- package/build/angular/index.js +15 -1
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +5 -6
- package/build/angular/modules/animated/create-animated-component.js +60 -73
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/animated/index.js +1 -1
- package/build/angular/modules/animated/index.js.map +1 -1
- package/build/angular/modules/image/index.d.ts +3 -0
- package/build/angular/modules/image/index.js +16 -0
- package/build/angular/modules/image/index.js.map +1 -0
- package/build/angular/primitives/index.d.ts +18 -18
- package/build/angular/primitives/index.js +40 -36
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +2 -3
- package/build/angular/primitives/shared.js +8 -53
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/register.d.ts +1 -1
- package/build/angular/register.js +12 -31
- package/build/angular/register.js.map +1 -1
- package/build/angular/render/index.js +3 -3
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/renderer/index.js +113 -67
- package/build/angular/renderer/index.js.map +1 -1
- package/build/angular/renderer/value-change.d.ts +2 -0
- package/build/angular/renderer/value-change.js +9 -0
- package/build/angular/renderer/value-change.js.map +1 -0
- package/metro-config.cjs +23 -5
- package/metro-css-parser.cjs +2 -1
- package/package.json +5 -14
- package/src/anchor-host-registry.ts +1 -17
- package/src/change-detection-flush.ts +120 -0
- package/src/components/activity-indicator-props.ts +21 -0
- package/src/components/button-props.ts +26 -0
- package/src/components/flat-list/index.ts +12 -4
- package/src/components/image-background-props.ts +24 -0
- package/src/components/{image/shared.ts → image-shared.ts} +6 -1
- package/src/components/input-accessory-view-props.ts +21 -0
- package/src/components/keyboard-avoiding-view/index.ts +6 -9
- package/src/components/modal/index.ts +15 -7
- package/src/components/pressable-props.ts +73 -0
- package/src/components/refresh-control-props.ts +43 -0
- package/src/components/safe-area-view-props.ts +28 -0
- package/src/components/scroll-view-props.ts +109 -0
- package/src/components/text-input-props.ts +72 -0
- package/src/components/touchable-native-feedback/touchable-native-feedback-props.ts +23 -0
- package/src/components/touchable-props.ts +30 -0
- package/src/components/touchable-without-feedback/touchable-without-feedback-props.ts +21 -0
- package/src/components/virtualized-list/index.ts +239 -156
- package/src/components.ts +84 -35
- package/src/create-portal/index.ts +5 -5
- package/src/create-tunnel/index.ts +1 -1
- package/src/diagnostics.ts +1 -1
- package/src/element-props.ts +90 -0
- package/src/elements.ts +919 -0
- package/src/host-instance/index.ts +1 -1
- package/src/index.ts +52 -15
- package/src/modules/animated/create-animated-component.ts +50 -55
- package/src/modules/animated/index.ts +1 -1
- package/src/modules/image/index.ts +18 -0
- package/src/primitives/index.ts +22 -18
- package/src/primitives/shared.ts +11 -44
- package/src/register.ts +12 -40
- package/src/render/index.ts +3 -3
- package/src/renderer/index.ts +122 -73
- package/src/renderer/value-change.ts +8 -0
- package/babel-lower-host-primitives.cjs +0 -636
- package/build/angular/components/activity-indicator/index.android.d.ts +0 -13
- package/build/angular/components/activity-indicator/index.android.js +0 -29
- package/build/angular/components/activity-indicator/index.android.js.map +0 -1
- package/build/angular/components/activity-indicator/index.d.ts +0 -1
- package/build/angular/components/activity-indicator/index.ios.d.ts +0 -9
- package/build/angular/components/activity-indicator/index.ios.js +0 -26
- package/build/angular/components/activity-indicator/index.ios.js.map +0 -1
- package/build/angular/components/activity-indicator/index.js +0 -4
- package/build/angular/components/activity-indicator/index.js.map +0 -1
- package/build/angular/components/activity-indicator/shared.d.ts +0 -67
- package/build/angular/components/activity-indicator/shared.js +0 -260
- package/build/angular/components/activity-indicator/shared.js.map +0 -1
- package/build/angular/components/button.d.ts +0 -68
- package/build/angular/components/button.js +0 -360
- package/build/angular/components/button.js.map +0 -1
- package/build/angular/components/image/index.android.d.ts +0 -15
- package/build/angular/components/image/index.android.js +0 -83
- package/build/angular/components/image/index.android.js.map +0 -1
- package/build/angular/components/image/index.d.ts +0 -1
- package/build/angular/components/image/index.ios.d.ts +0 -15
- package/build/angular/components/image/index.ios.js +0 -83
- package/build/angular/components/image/index.ios.js.map +0 -1
- package/build/angular/components/image/index.js +0 -3
- package/build/angular/components/image/index.js.map +0 -1
- package/build/angular/components/image/shared.js.map +0 -1
- package/build/angular/components/image-background.d.ts +0 -85
- package/build/angular/components/image-background.js +0 -443
- package/build/angular/components/image-background.js.map +0 -1
- package/build/angular/components/input-accessory-view/index.d.ts +0 -66
- package/build/angular/components/input-accessory-view/index.js +0 -287
- package/build/angular/components/input-accessory-view/index.js.map +0 -1
- package/build/angular/components/pressable/index.d.ts +0 -134
- package/build/angular/components/pressable/index.js +0 -514
- package/build/angular/components/pressable/index.js.map +0 -1
- package/build/angular/components/refresh-control/index.d.ts +0 -91
- package/build/angular/components/refresh-control/index.js +0 -382
- package/build/angular/components/refresh-control/index.js.map +0 -1
- package/build/angular/components/safe-area-view/index.d.ts +0 -64
- package/build/angular/components/safe-area-view/index.js +0 -287
- package/build/angular/components/safe-area-view/index.js.map +0 -1
- package/build/angular/components/scroll-view/index.android.d.ts +0 -20
- package/build/angular/components/scroll-view/index.android.js +0 -262
- package/build/angular/components/scroll-view/index.android.js.map +0 -1
- package/build/angular/components/scroll-view/index.d.ts +0 -3
- package/build/angular/components/scroll-view/index.ios.d.ts +0 -8
- package/build/angular/components/scroll-view/index.ios.js +0 -92
- package/build/angular/components/scroll-view/index.ios.js.map +0 -1
- package/build/angular/components/scroll-view/index.js +0 -6
- package/build/angular/components/scroll-view/index.js.map +0 -1
- package/build/angular/components/scroll-view/projection.d.ts +0 -67
- package/build/angular/components/scroll-view/projection.js +0 -560
- package/build/angular/components/scroll-view/projection.js.map +0 -1
- package/build/angular/components/scroll-view/shared.d.ts +0 -241
- package/build/angular/components/scroll-view/shared.js +0 -848
- package/build/angular/components/scroll-view/shared.js.map +0 -1
- package/build/angular/components/scroll-view/sticky-header.d.ts +0 -40
- package/build/angular/components/scroll-view/sticky-header.js +0 -224
- package/build/angular/components/scroll-view/sticky-header.js.map +0 -1
- package/build/angular/components/switch/index.android.d.ts +0 -24
- package/build/angular/components/switch/index.android.js +0 -112
- package/build/angular/components/switch/index.android.js.map +0 -1
- package/build/angular/components/switch/index.d.ts +0 -1
- package/build/angular/components/switch/index.ios.d.ts +0 -23
- package/build/angular/components/switch/index.ios.js +0 -111
- package/build/angular/components/switch/index.ios.js.map +0 -1
- package/build/angular/components/switch/index.js +0 -4
- package/build/angular/components/switch/index.js.map +0 -1
- package/build/angular/components/switch/shared.d.ts +0 -49
- package/build/angular/components/switch/shared.js +0 -191
- package/build/angular/components/switch/shared.js.map +0 -1
- package/build/angular/components/text-input.d.ts +0 -167
- package/build/angular/components/text-input.js +0 -684
- package/build/angular/components/text-input.js.map +0 -1
- package/build/angular/components/touchable/index.d.ts +0 -283
- package/build/angular/components/touchable/index.js +0 -1453
- package/build/angular/components/touchable/index.js.map +0 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +0 -87
- package/build/angular/components/touchable-native-feedback/index.js +0 -409
- package/build/angular/components/touchable-native-feedback/index.js.map +0 -1
- package/build/angular/state-style.d.ts +0 -9
- package/build/angular/state-style.js +0 -39
- package/build/angular/state-style.js.map +0 -1
- package/metro-transformer.cjs +0 -69
- package/src/components/activity-indicator/index.android.ts +0 -23
- package/src/components/activity-indicator/index.ios.ts +0 -21
- package/src/components/activity-indicator/index.ts +0 -4
- package/src/components/activity-indicator/shared.ts +0 -225
- package/src/components/button.ts +0 -237
- package/src/components/image/index.android.ts +0 -95
- package/src/components/image/index.ios.ts +0 -95
- package/src/components/image/index.ts +0 -3
- package/src/components/image-background.ts +0 -308
- package/src/components/input-accessory-view/index.ts +0 -246
- package/src/components/pressable/index.ts +0 -522
- package/src/components/refresh-control/index.ts +0 -349
- package/src/components/safe-area-view/index.ts +0 -247
- package/src/components/scroll-view/index.android.ts +0 -205
- package/src/components/scroll-view/index.ios.ts +0 -81
- package/src/components/scroll-view/index.ts +0 -7
- package/src/components/scroll-view/projection.ts +0 -713
- package/src/components/scroll-view/shared.ts +0 -1093
- package/src/components/scroll-view/sticky-header.ts +0 -267
- package/src/components/switch/index.android.ts +0 -113
- package/src/components/switch/index.ios.ts +0 -112
- package/src/components/switch/index.ts +0 -4
- package/src/components/switch/shared.ts +0 -284
- package/src/components/text-input.ts +0 -669
- package/src/components/touchable/index.ts +0 -956
- package/src/components/touchable-native-feedback/index.ts +0 -327
- package/src/state-style.ts +0 -39
|
@@ -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,23 +22,27 @@
|
|
|
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
|
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, Input, Output, ViewChild, computed, inject, signal, } from '@angular/core';
|
|
27
|
-
import { DEFAULT_END_REACHED_THRESHOLD, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_START_REACHED_THRESHOLD, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_WINDOW_SIZE, EMPTY_OFFSET, FIRST_INDEX, INVERTED_X_STYLE, INVERTED_Y_STYLE, buildListPlan, buildViewabilityPairs, computeWindow, createInitialListState, isSeparatorGapInRange, listEffectSignature, readLayoutLength, readLayoutOffset, readScrollOffset, reduceList, resolveAccessibilityProps, resolveItemKey, } from '@symbiote-native/components';
|
|
28
|
-
import { dlog, flattenStyle, } from '@symbiote-native/engine';
|
|
27
|
+
import { DEFAULT_END_REACHED_THRESHOLD, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_START_REACHED_THRESHOLD, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_WINDOW_SIZE, EMPTY_OFFSET, FIRST_INDEX, INVERTED_X_STYLE, INVERTED_Y_STYLE, buildListPlan, buildViewabilityPairs, computeWindow, createInitialListState, isSeparatorGapInRange, listEffectSignature, readLayoutLength, readLayoutOffset, readScrollOffset, reduceList, resolveAccessibilityProps, resolveItemKey, buildScrollViewHandle, } from '@symbiote-native/components';
|
|
28
|
+
import { dlog, flattenStyle, isSymbioteNode, } from '@symbiote-native/engine';
|
|
29
29
|
import { countAngular } from '../../diagnostics';
|
|
30
|
-
import {
|
|
31
|
-
import { RefreshControl } from '../refresh-control';
|
|
32
|
-
import { anchorHostStyle, SymbioteStyleInputDirective, ViewHost, } from '../../primitives';
|
|
30
|
+
import { anchorHostStyle, SymbioteHostPropsDirective, SymbioteStyleInputDirective, ViewHost, } from '../../primitives';
|
|
33
31
|
import { VListEmptyDirective, VListFooterDirective, VListHeaderDirective, VListItemDirective, VListOutletDirective, VListSeparatorDirective, } from './directives';
|
|
34
|
-
import { gateWanted, injectGateDemandAbove,
|
|
32
|
+
import { gateWanted, injectGateDemandAbove, } from '../../gate-demand';
|
|
35
33
|
import * as i0 from "@angular/core";
|
|
36
34
|
import * as i1 from "../../primitives/shared";
|
|
37
35
|
export { VListEmptyDirective, VListFooterDirective, VListHeaderDirective, VListItemDirective, VListSeparatorDirective, } from './directives';
|
|
36
|
+
// The scroll body is identical on both axes; only the outer tag differs (the axis comes from the
|
|
37
|
+
// TAG, never a prop — `<horizontal-scroll-view>` vs `<scroll-view>`, matching every other adapter,
|
|
38
|
+
// `.claude/rules/host-primitive-tier.md`). No shared-outlet primitive exists to de-duplicate this
|
|
39
|
+
// (this package's own NgTemplateOutlet twin is keyed on a per-cell context, not a static block, and
|
|
40
|
+
// ngc's partial evaluator needs the decorator's `template` to be one literal), so the body is
|
|
41
|
+
// duplicated across the two branches — the same shape Svelte's own `{#if horizontal}` takes.
|
|
38
42
|
export class VirtualizedList {
|
|
39
43
|
// The list's edge/viewability/failure events as real Angular events: `(endReached)="…"`, not
|
|
40
44
|
// `[onEndReached]="…"`. See handleRefresh/accessibility*Tick below for how the still-callback-
|
|
41
|
-
// shaped
|
|
45
|
+
// shaped scroll-view/refresh-control bag keys are fed from these.
|
|
42
46
|
endReached = new EventEmitter();
|
|
43
47
|
startReached = new EventEmitter();
|
|
44
48
|
refresh = new EventEmitter();
|
|
@@ -137,10 +141,19 @@ export class VirtualizedList {
|
|
|
137
141
|
footerDir;
|
|
138
142
|
emptyDir;
|
|
139
143
|
separatorDir;
|
|
140
|
-
// The
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
+
// The inner scroll TAG's own engine node — a template ref on a bare intrinsic hands back the
|
|
145
|
+
// host node directly (`isSymbioteNode(elementRef.nativeElement)`, matching `anchorHostStyle`'s
|
|
146
|
+
// own reader below), so there is no wrapper component left to read a handle off. Present from
|
|
147
|
+
// whichever branch (`scroll-view` / `horizontal-scroll-view`) is currently mounted, once the
|
|
148
|
+
// template has run at least one CD pass.
|
|
149
|
+
scrollHostRef;
|
|
150
|
+
get scrollNode() {
|
|
151
|
+
const node = this.scrollHostRef?.nativeElement;
|
|
152
|
+
return isSymbioteNode(node) ? node : null;
|
|
153
|
+
}
|
|
154
|
+
// `buildScrollViewHandle` reads the node lazily through the getter above and no-ops before
|
|
155
|
+
// commit — the same function every other adapter feeds from its own host-instance accessor.
|
|
156
|
+
scrollHandle = buildScrollViewHandle(() => this.scrollNode);
|
|
144
157
|
// --- template-bound view state, assembled in ngDoCheck (recomputeView) ---
|
|
145
158
|
itemCount = EMPTY_OFFSET;
|
|
146
159
|
windowCells = [];
|
|
@@ -209,12 +222,12 @@ export class VirtualizedList {
|
|
|
209
222
|
lastRecompute = undefined;
|
|
210
223
|
cdr = inject(ChangeDetectorRef);
|
|
211
224
|
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
212
|
-
// onto (see anchorHostStyle's doc comment) — NOT `
|
|
213
|
-
// inner `<
|
|
225
|
+
// onto (see anchorHostStyle's doc comment) — NOT `scrollHostRef` above, which targets the real
|
|
226
|
+
// inner `<scroll-view>`/`<horizontal-scroll-view>` tag one level down.
|
|
214
227
|
elementRef = inject(ElementRef);
|
|
215
|
-
// Bridges the non-reactive @Input fields into the reactive graph so
|
|
216
|
-
// memoize a bag derived from them (
|
|
217
|
-
//
|
|
228
|
+
// Bridges the non-reactive @Input fields into the reactive graph so `foldedAccessibility` below
|
|
229
|
+
// can memoize a bag derived from them (read its own comment for why signal inputs are not an
|
|
230
|
+
// option while this package's unit suite runs on JIT).
|
|
218
231
|
//
|
|
219
232
|
// ONLY safe for a bag whose every dependency is an @Input. This list's window machinery
|
|
220
233
|
// (listState, renderVersion, windowCells, the measured metrics) is driven from scroll/layout
|
|
@@ -223,12 +236,13 @@ export class VirtualizedList {
|
|
|
223
236
|
// instead of widening this one.
|
|
224
237
|
inputsRevision = signal(0, /* @ts-ignore */
|
|
225
238
|
...(ngDevMode ? [{ debugName: "inputsRevision" }] : /* istanbul ignore next */ []));
|
|
226
|
-
// MEASURED: the template
|
|
227
|
-
// not cache a getter across binding expressions - as a getter it
|
|
228
|
-
// refresh, and every consumer saw a fresh reference, so all 20
|
|
229
|
-
//
|
|
230
|
-
// input actually changes. Every dependency below is an @Input, which is what makes
|
|
231
|
-
// inputsRevision a complete dependency set here
|
|
239
|
+
// MEASURED (while ScrollView was still a wrapper): the template fed 20 separate inputs off this
|
|
240
|
+
// one bag, and Angular does not cache a getter across binding expressions - as a getter it
|
|
241
|
+
// rebuilt the object 20 times per refresh, and every consumer saw a fresh reference, so all 20
|
|
242
|
+
// reported "changed" and wrote through. A computed evaluates once and hands back the SAME object
|
|
243
|
+
// until an input actually changes. Every dependency below is an @Input, which is what makes
|
|
244
|
+
// inputsRevision a complete dependency set here — `scrollViewBag()` reuses this result rather
|
|
245
|
+
// than re-running the fold.
|
|
232
246
|
foldedAccessibility = computed(() => {
|
|
233
247
|
this.inputsRevision();
|
|
234
248
|
return resolveAccessibilityProps({
|
|
@@ -273,6 +287,54 @@ export class VirtualizedList {
|
|
|
273
287
|
get shouldRenderRefreshControl() {
|
|
274
288
|
return this.refreshRequested ?? this.refresh.observed;
|
|
275
289
|
}
|
|
290
|
+
// The flat bag fed to the scroll tag via `[symbioteHostProps]`. A plain method, not `computed()`:
|
|
291
|
+
// most of these fields (resolvedStyle, renderedStickyIndices, commandedOffset, …) are recomputed
|
|
292
|
+
// in ngDoCheck/dispatch as ordinary fields, not signals, so a computed() memoized only against
|
|
293
|
+
// `inputsRevision` would go stale the moment one of them changed outside an @Input() write. The
|
|
294
|
+
// directive itself diffs per key before writing to the engine (`primitives/shared.ts`), so a
|
|
295
|
+
// fresh object every CD pass costs nothing beyond the literal allocation.
|
|
296
|
+
scrollViewBag() {
|
|
297
|
+
return {
|
|
298
|
+
...this.foldedAccessibility(),
|
|
299
|
+
style: this.resolvedStyle,
|
|
300
|
+
contentContainerStyle: this.resolvedContentContainerStyle,
|
|
301
|
+
onScroll: this.onScrollTick,
|
|
302
|
+
onLayout: this.onLayoutTick,
|
|
303
|
+
onScrollBeginDrag: this.onScrollBeginDrag,
|
|
304
|
+
onScrollEndDrag: this.onScrollEndDrag,
|
|
305
|
+
onMomentumScrollBegin: this.onMomentumScrollBegin,
|
|
306
|
+
onMomentumScrollEnd: this.onMomentumScrollEnd,
|
|
307
|
+
scrollEventThrottle: this.scrollEventThrottle,
|
|
308
|
+
keyboardShouldPersistTaps: this.keyboardShouldPersistTaps,
|
|
309
|
+
keyboardDismissMode: this.keyboardDismissMode,
|
|
310
|
+
contentOffset: this.commandedOffset,
|
|
311
|
+
stickyHeaderIndices: this.renderedStickyIndices,
|
|
312
|
+
maintainVisibleContentPosition: this.resolvedMaintainVisibleContentPosition,
|
|
313
|
+
// The four boolean-gated Fabric events (`.claude/rules/fabric-boolean-event-gates.md`): the
|
|
314
|
+
// TAG has no `.observed` of its own, so `wantsGate` (own subscriber, or the demand from
|
|
315
|
+
// whatever wraps this list) is what decides whether the flag reaches Fabric at all.
|
|
316
|
+
onAccessibilityAction: this.wantsGate('accessibilityAction')
|
|
317
|
+
? this.accessibilityActionTick
|
|
318
|
+
: undefined,
|
|
319
|
+
onAccessibilityTap: this.wantsGate('accessibilityTap')
|
|
320
|
+
? this.accessibilityTapTick
|
|
321
|
+
: undefined,
|
|
322
|
+
onMagicTap: this.wantsGate('magicTap') ? this.magicTapTick : undefined,
|
|
323
|
+
onAccessibilityEscape: this.wantsGate('accessibilityEscape')
|
|
324
|
+
? this.accessibilityEscapeTick
|
|
325
|
+
: undefined,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
// The `refresh-control` tag's own bag — an ordinary first child, re-parented per platform by the
|
|
329
|
+
// scroll-view behavior (`../refresh-control-props.ts`). `onRefresh` is a plain listener prop, not
|
|
330
|
+
// a gated one: RefreshControl's `refresh` is unaffected by `fabric-boolean-event-gates.md`.
|
|
331
|
+
refreshControlBag() {
|
|
332
|
+
return {
|
|
333
|
+
refreshing: this.refreshing ?? false,
|
|
334
|
+
progressViewOffset: this.progressViewOffset,
|
|
335
|
+
onRefresh: this.handleRefresh,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
276
338
|
get isHorizontal() {
|
|
277
339
|
return this.horizontal === true;
|
|
278
340
|
}
|
|
@@ -297,9 +359,10 @@ export class VirtualizedList {
|
|
|
297
359
|
get onStartReachedThresholdValue() {
|
|
298
360
|
return this.onStartReachedThreshold ?? DEFAULT_START_REACHED_THRESHOLD;
|
|
299
361
|
}
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
362
|
+
// onScroll stays a plain callback bag key (an Animated.event(...) target must be able to flow
|
|
363
|
+
// through it, which an @Output() can't carry), so this is a stable arrow field read straight into
|
|
364
|
+
// scrollViewBag()'s `onScroll`. onLayout is unconditional too — this list always needs its own
|
|
365
|
+
// layout for windowing, regardless of whether an app subscribes to anything.
|
|
303
366
|
onScrollTick = (event) => {
|
|
304
367
|
countAngular('scrollTicks');
|
|
305
368
|
const offset = readScrollOffset(event, this.isHorizontal);
|
|
@@ -319,9 +382,9 @@ export class VirtualizedList {
|
|
|
319
382
|
dlog(`Angular VirtualizedList onLayout viewport=${length}`);
|
|
320
383
|
this.dispatch({ kind: 'layout', length });
|
|
321
384
|
};
|
|
322
|
-
// RefreshControl's refresh and
|
|
323
|
-
//
|
|
324
|
-
//
|
|
385
|
+
// RefreshControl's refresh and the scroll tag's accessibility events reach here as plain
|
|
386
|
+
// listener-prop values in scrollViewBag()/refreshControlBag() — these fields just adapt
|
|
387
|
+
// VirtualizedList's own @Output() into a plain re-emit callback.
|
|
325
388
|
handleRefresh = () => {
|
|
326
389
|
this.refresh.emit();
|
|
327
390
|
};
|
|
@@ -789,19 +852,19 @@ export class VirtualizedList {
|
|
|
789
852
|
});
|
|
790
853
|
}
|
|
791
854
|
flashScrollIndicators() {
|
|
792
|
-
this.
|
|
855
|
+
this.scrollHandle.flashScrollIndicators();
|
|
793
856
|
}
|
|
794
857
|
getNativeScrollRef() {
|
|
795
|
-
return this.
|
|
858
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
796
859
|
}
|
|
797
860
|
getScrollableNode() {
|
|
798
|
-
return this.
|
|
861
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
799
862
|
}
|
|
800
863
|
getScrollResponder() {
|
|
801
|
-
return this.
|
|
864
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
802
865
|
}
|
|
803
866
|
getScrollNode() {
|
|
804
|
-
return this.
|
|
867
|
+
return this.scrollHandle.getScrollNode();
|
|
805
868
|
}
|
|
806
869
|
recordInteraction() {
|
|
807
870
|
this.dispatch({ kind: 'record-interaction' });
|
|
@@ -811,9 +874,9 @@ export class VirtualizedList {
|
|
|
811
874
|
const target = this.isHorizontal
|
|
812
875
|
? { x: clamped, y: EMPTY_OFFSET }
|
|
813
876
|
: { x: EMPTY_OFFSET, y: clamped };
|
|
814
|
-
if (this.
|
|
877
|
+
if (this.scrollNode !== null) {
|
|
815
878
|
dlog(`Angular VirtualizedList scrollTo offset=${clamped} animated=${animated} (horizontal=${this.isHorizontal})`);
|
|
816
|
-
this.
|
|
879
|
+
this.scrollHandle.scrollTo({ x: target.x, y: target.y, animated });
|
|
817
880
|
return;
|
|
818
881
|
}
|
|
819
882
|
dlog(`Angular VirtualizedList scrollTo offset=${clamped} pending-ref`);
|
|
@@ -821,278 +884,301 @@ export class VirtualizedList {
|
|
|
821
884
|
this.cdr.markForCheck();
|
|
822
885
|
}
|
|
823
886
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: VirtualizedList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
824
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: VirtualizedList, isStandalone: true, selector: "VirtualizedList", inputs: { data: "data", getItem: "getItem", getItemCount: "getItemCount", keyExtractor: "keyExtractor", getItemLayout: "getItemLayout", horizontal: "horizontal", inverted: "inverted", extraData: "extraData", onEndReachedThreshold: "onEndReachedThreshold", onStartReachedThreshold: "onStartReachedThreshold", refreshing: "refreshing", progressViewOffset: "progressViewOffset", refreshRequested: "refreshRequested", viewabilityConfig: "viewabilityConfig", viewabilityConfigCallbackPairs: "viewabilityConfigCallbackPairs", initialNumToRender: "initialNumToRender", initialScrollIndex: "initialScrollIndex", maxToRenderPerBatch: "maxToRenderPerBatch", updateCellsBatchingPeriod: "updateCellsBatchingPeriod", windowSize: "windowSize", stickyHeaderIndices: "stickyHeaderIndices", maintainVisibleContentPosition: "maintainVisibleContentPosition", onScroll: "onScroll", onScrollBeginDrag: "onScrollBeginDrag", onScrollEndDrag: "onScrollEndDrag", onMomentumScrollBegin: "onMomentumScrollBegin", onMomentumScrollEnd: "onMomentumScrollEnd", scrollEventThrottle: "scrollEventThrottle", keyboardShouldPersistTaps: "keyboardShouldPersistTaps", keyboardDismissMode: "keyboardDismissMode", style: "style", contentContainerStyle: "contentContainerStyle", testID: "testID", nativeID: "nativeID", accessible: "accessible", accessibilityLabel: "accessibilityLabel", accessibilityHint: "accessibilityHint", accessibilityRole: "accessibilityRole", accessibilityState: "accessibilityState", accessibilityValue: "accessibilityValue", accessibilityActions: "accessibilityActions", accessibilityLabelledBy: "accessibilityLabelledBy", importantForAccessibility: "importantForAccessibility", accessibilityLiveRegion: "accessibilityLiveRegion", screenReaderFocusable: "screenReaderFocusable", accessibilityViewIsModal: "accessibilityViewIsModal", accessibilityElementsHidden: "accessibilityElementsHidden", accessibilityIgnoresInvertColors: "accessibilityIgnoresInvertColors", accessibilityLanguage: "accessibilityLanguage", accessibilityRespondsToUserInteraction: "accessibilityRespondsToUserInteraction", accessibilityShowsLargeContentViewer: "accessibilityShowsLargeContentViewer", accessibilityLargeContentTitle: "accessibilityLargeContentTitle", role: "role", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", ariaLive: "ariaLive", ariaHidden: "ariaHidden", ariaBusy: "ariaBusy", ariaChecked: "ariaChecked", ariaDisabled: "ariaDisabled", ariaExpanded: "ariaExpanded", ariaSelected: "ariaSelected", ariaModal: "ariaModal", ariaValueMax: "ariaValueMax", ariaValueMin: "ariaValueMin", ariaValueNow: "ariaValueNow", ariaValueText: "ariaValueText" }, outputs: { endReached: "endReached", startReached: "startReached", refresh: "refresh", viewableItemsChanged: "viewableItemsChanged", scrollToIndexFailed: "scrollToIndexFailed", accessibilityAction: "accessibilityAction", accessibilityTap: "accessibilityTap", magicTap: "magicTap", accessibilityEscape: "accessibilityEscape" }, queries: [{ propertyName: "itemDir", first: true, predicate: VListItemDirective, descendants: true }, { propertyName: "headerDir", first: true, predicate: VListHeaderDirective, descendants: true }, { propertyName: "footerDir", first: true, predicate: VListFooterDirective, descendants: true }, { propertyName: "emptyDir", first: true, predicate: VListEmptyDirective, descendants: true }, { propertyName: "separatorDir", first: true, predicate: VListSeparatorDirective, descendants: true }], viewQueries: [{ propertyName: "
|
|
825
|
-
|
|
826
|
-
[
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
[accessibilityLabel]="foldedAccessibility().accessibilityLabel"
|
|
833
|
-
[accessibilityHint]="foldedAccessibility().accessibilityHint"
|
|
834
|
-
[accessibilityRole]="foldedAccessibility().accessibilityRole"
|
|
835
|
-
[accessibilityState]="foldedAccessibility().accessibilityState"
|
|
836
|
-
[accessibilityValue]="foldedAccessibility().accessibilityValue"
|
|
837
|
-
[accessibilityActions]="foldedAccessibility().accessibilityActions"
|
|
838
|
-
[accessibilityLabelledBy]="foldedAccessibility().accessibilityLabelledBy"
|
|
839
|
-
[importantForAccessibility]="
|
|
840
|
-
foldedAccessibility().importantForAccessibility
|
|
841
|
-
"
|
|
842
|
-
[accessibilityLiveRegion]="foldedAccessibility().accessibilityLiveRegion"
|
|
843
|
-
[screenReaderFocusable]="foldedAccessibility().screenReaderFocusable"
|
|
844
|
-
[accessibilityViewIsModal]="
|
|
845
|
-
foldedAccessibility().accessibilityViewIsModal
|
|
846
|
-
"
|
|
847
|
-
[accessibilityElementsHidden]="
|
|
848
|
-
foldedAccessibility().accessibilityElementsHidden
|
|
849
|
-
"
|
|
850
|
-
[accessibilityIgnoresInvertColors]="
|
|
851
|
-
foldedAccessibility().accessibilityIgnoresInvertColors
|
|
852
|
-
"
|
|
853
|
-
[accessibilityLanguage]="foldedAccessibility().accessibilityLanguage"
|
|
854
|
-
[accessibilityRespondsToUserInteraction]="
|
|
855
|
-
foldedAccessibility().accessibilityRespondsToUserInteraction
|
|
856
|
-
"
|
|
857
|
-
[accessibilityShowsLargeContentViewer]="
|
|
858
|
-
foldedAccessibility().accessibilityShowsLargeContentViewer
|
|
859
|
-
"
|
|
860
|
-
[accessibilityLargeContentTitle]="
|
|
861
|
-
foldedAccessibility().accessibilityLargeContentTitle
|
|
862
|
-
"
|
|
863
|
-
(accessibilityAction)="accessibilityActionTick($event)"
|
|
864
|
-
(accessibilityTap)="accessibilityTapTick($event)"
|
|
865
|
-
(magicTap)="magicTapTick($event)"
|
|
866
|
-
(accessibilityEscape)="accessibilityEscapeTick($event)"
|
|
867
|
-
[onScroll]="onScrollTick"
|
|
868
|
-
(layout)="onLayoutTick($event)"
|
|
869
|
-
[onScrollBeginDrag]="onScrollBeginDrag"
|
|
870
|
-
[onScrollEndDrag]="onScrollEndDrag"
|
|
871
|
-
[onMomentumScrollBegin]="onMomentumScrollBegin"
|
|
872
|
-
[onMomentumScrollEnd]="onMomentumScrollEnd"
|
|
873
|
-
[scrollEventThrottle]="scrollEventThrottle"
|
|
874
|
-
[keyboardShouldPersistTaps]="keyboardShouldPersistTaps"
|
|
875
|
-
[keyboardDismissMode]="keyboardDismissMode"
|
|
876
|
-
[contentOffset]="commandedOffset"
|
|
877
|
-
[stickyHeaderIndices]="renderedStickyIndices"
|
|
878
|
-
[maintainVisibleContentPosition]="resolvedMaintainVisibleContentPosition"
|
|
879
|
-
>
|
|
880
|
-
@if (shouldRenderRefreshControl) {
|
|
881
|
-
<RefreshControl
|
|
882
|
-
[refreshing]="refreshing ?? false"
|
|
883
|
-
(refresh)="handleRefresh()"
|
|
884
|
-
[progressViewOffset]="progressViewOffset"
|
|
885
|
-
/>
|
|
886
|
-
}
|
|
887
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: VirtualizedList, isStandalone: true, selector: "VirtualizedList", inputs: { data: "data", getItem: "getItem", getItemCount: "getItemCount", keyExtractor: "keyExtractor", getItemLayout: "getItemLayout", horizontal: "horizontal", inverted: "inverted", extraData: "extraData", onEndReachedThreshold: "onEndReachedThreshold", onStartReachedThreshold: "onStartReachedThreshold", refreshing: "refreshing", progressViewOffset: "progressViewOffset", refreshRequested: "refreshRequested", viewabilityConfig: "viewabilityConfig", viewabilityConfigCallbackPairs: "viewabilityConfigCallbackPairs", initialNumToRender: "initialNumToRender", initialScrollIndex: "initialScrollIndex", maxToRenderPerBatch: "maxToRenderPerBatch", updateCellsBatchingPeriod: "updateCellsBatchingPeriod", windowSize: "windowSize", stickyHeaderIndices: "stickyHeaderIndices", maintainVisibleContentPosition: "maintainVisibleContentPosition", onScroll: "onScroll", onScrollBeginDrag: "onScrollBeginDrag", onScrollEndDrag: "onScrollEndDrag", onMomentumScrollBegin: "onMomentumScrollBegin", onMomentumScrollEnd: "onMomentumScrollEnd", scrollEventThrottle: "scrollEventThrottle", keyboardShouldPersistTaps: "keyboardShouldPersistTaps", keyboardDismissMode: "keyboardDismissMode", style: "style", contentContainerStyle: "contentContainerStyle", testID: "testID", nativeID: "nativeID", accessible: "accessible", accessibilityLabel: "accessibilityLabel", accessibilityHint: "accessibilityHint", accessibilityRole: "accessibilityRole", accessibilityState: "accessibilityState", accessibilityValue: "accessibilityValue", accessibilityActions: "accessibilityActions", accessibilityLabelledBy: "accessibilityLabelledBy", importantForAccessibility: "importantForAccessibility", accessibilityLiveRegion: "accessibilityLiveRegion", screenReaderFocusable: "screenReaderFocusable", accessibilityViewIsModal: "accessibilityViewIsModal", accessibilityElementsHidden: "accessibilityElementsHidden", accessibilityIgnoresInvertColors: "accessibilityIgnoresInvertColors", accessibilityLanguage: "accessibilityLanguage", accessibilityRespondsToUserInteraction: "accessibilityRespondsToUserInteraction", accessibilityShowsLargeContentViewer: "accessibilityShowsLargeContentViewer", accessibilityLargeContentTitle: "accessibilityLargeContentTitle", role: "role", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", ariaLive: "ariaLive", ariaHidden: "ariaHidden", ariaBusy: "ariaBusy", ariaChecked: "ariaChecked", ariaDisabled: "ariaDisabled", ariaExpanded: "ariaExpanded", ariaSelected: "ariaSelected", ariaModal: "ariaModal", ariaValueMax: "ariaValueMax", ariaValueMin: "ariaValueMin", ariaValueNow: "ariaValueNow", ariaValueText: "ariaValueText" }, outputs: { endReached: "endReached", startReached: "startReached", refresh: "refresh", viewableItemsChanged: "viewableItemsChanged", scrollToIndexFailed: "scrollToIndexFailed", accessibilityAction: "accessibilityAction", accessibilityTap: "accessibilityTap", magicTap: "magicTap", accessibilityEscape: "accessibilityEscape" }, queries: [{ propertyName: "itemDir", first: true, predicate: VListItemDirective, descendants: true }, { propertyName: "headerDir", first: true, predicate: VListHeaderDirective, descendants: true }, { propertyName: "footerDir", first: true, predicate: VListFooterDirective, descendants: true }, { propertyName: "emptyDir", first: true, predicate: VListEmptyDirective, descendants: true }, { propertyName: "separatorDir", first: true, predicate: VListSeparatorDirective, descendants: true }], viewQueries: [{ propertyName: "scrollHostRef", first: true, predicate: ["scrollHost"], descendants: true, read: ElementRef }], usesOnChanges: true, hostDirectives: [{ directive: i1.SymbioteStyleInputDirective, inputs: ["style", "style"] }], ngImport: i0, template: `
|
|
888
|
+
@if (isHorizontal) {
|
|
889
|
+
<horizontal-scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
890
|
+
@if (shouldRenderRefreshControl) {
|
|
891
|
+
<refresh-control
|
|
892
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
893
|
+
></refresh-control>
|
|
894
|
+
}
|
|
887
895
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
896
|
+
@if (headerDir !== undefined) {
|
|
897
|
+
<view>
|
|
898
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
899
|
+
</view>
|
|
900
|
+
}
|
|
893
901
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
902
|
+
@if (itemCount === 0) {
|
|
903
|
+
@if (emptyDir !== undefined) {
|
|
904
|
+
<view>
|
|
905
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
906
|
+
</view>
|
|
907
|
+
}
|
|
908
|
+
} @else {
|
|
909
|
+
@if (leadingSpacerStyle !== null) {
|
|
910
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
911
|
+
}
|
|
912
|
+
@if (forcedStickyCell !== null) {
|
|
913
|
+
<view
|
|
914
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
915
|
+
[style]="cellStyle"
|
|
916
|
+
>
|
|
917
|
+
<ng-container
|
|
918
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
919
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
920
|
+
></ng-container>
|
|
921
|
+
</view>
|
|
922
|
+
}
|
|
923
|
+
@if (gapSpacerStyle !== null) {
|
|
924
|
+
<view [style]="gapSpacerStyle"></view>
|
|
925
|
+
}
|
|
926
|
+
@for (cell of windowCells; track cell.key) {
|
|
927
|
+
<view
|
|
928
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
929
|
+
[style]="cellStyle"
|
|
930
|
+
>
|
|
931
|
+
<ng-container
|
|
932
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
933
|
+
[vListOutletContext]="cell.context"
|
|
934
|
+
></ng-container>
|
|
935
|
+
@if (cell.separatorContext !== undefined) {
|
|
936
|
+
<ng-container
|
|
937
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
938
|
+
[vListOutletContext]="cell.separatorContext"
|
|
939
|
+
></ng-container>
|
|
940
|
+
}
|
|
941
|
+
</view>
|
|
942
|
+
}
|
|
943
|
+
@if (trailingSpacerStyle !== null) {
|
|
944
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
945
|
+
}
|
|
899
946
|
}
|
|
900
|
-
|
|
901
|
-
@if (
|
|
902
|
-
<
|
|
947
|
+
|
|
948
|
+
@if (footerDir !== undefined) {
|
|
949
|
+
<view>
|
|
950
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
951
|
+
</view>
|
|
903
952
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
[vListOutletContext]="forcedStickyCell.context"
|
|
912
|
-
></ng-container>
|
|
913
|
-
</symbiote-view>
|
|
953
|
+
</horizontal-scroll-view>
|
|
954
|
+
} @else {
|
|
955
|
+
<scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
956
|
+
@if (shouldRenderRefreshControl) {
|
|
957
|
+
<refresh-control
|
|
958
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
959
|
+
></refresh-control>
|
|
914
960
|
}
|
|
915
|
-
|
|
916
|
-
|
|
961
|
+
|
|
962
|
+
@if (headerDir !== undefined) {
|
|
963
|
+
<view>
|
|
964
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
965
|
+
</view>
|
|
917
966
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
@
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
<
|
|
931
|
-
|
|
932
|
-
[
|
|
933
|
-
|
|
934
|
-
@if (cell.separatorContext !== undefined) {
|
|
967
|
+
|
|
968
|
+
@if (itemCount === 0) {
|
|
969
|
+
@if (emptyDir !== undefined) {
|
|
970
|
+
<view>
|
|
971
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
972
|
+
</view>
|
|
973
|
+
}
|
|
974
|
+
} @else {
|
|
975
|
+
@if (leadingSpacerStyle !== null) {
|
|
976
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
977
|
+
}
|
|
978
|
+
@if (forcedStickyCell !== null) {
|
|
979
|
+
<view
|
|
980
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
981
|
+
[style]="cellStyle"
|
|
982
|
+
>
|
|
935
983
|
<ng-container
|
|
936
|
-
[vListOutlet]="
|
|
937
|
-
[vListOutletContext]="
|
|
984
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
985
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
938
986
|
></ng-container>
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
987
|
+
</view>
|
|
988
|
+
}
|
|
989
|
+
@if (gapSpacerStyle !== null) {
|
|
990
|
+
<view [style]="gapSpacerStyle"></view>
|
|
991
|
+
}
|
|
992
|
+
<!-- The separator sits INSIDE the measuring view, as RN's own cell renderer places it
|
|
993
|
+
(VirtualizedListCellRenderer.js:218-221). As a sibling it is an extra flex child, so
|
|
994
|
+
the chrome between two cells is gap + separator + gap while a spacer collapsing that
|
|
995
|
+
region replaces it with a single gap — every cell below the leading spacer then lands
|
|
996
|
+
short by (separator + gap) and the content jumps by that much whenever the window's
|
|
997
|
+
first index moves. Measured at exactly 17px on device 2026-08-19; see
|
|
998
|
+
.claude/rules/list-geometry-feedback-loop.md. -->
|
|
999
|
+
@for (cell of windowCells; track cell.key) {
|
|
1000
|
+
<view
|
|
1001
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
1002
|
+
[style]="cellStyle"
|
|
1003
|
+
>
|
|
1004
|
+
<ng-container
|
|
1005
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1006
|
+
[vListOutletContext]="cell.context"
|
|
1007
|
+
></ng-container>
|
|
1008
|
+
@if (cell.separatorContext !== undefined) {
|
|
1009
|
+
<ng-container
|
|
1010
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
1011
|
+
[vListOutletContext]="cell.separatorContext"
|
|
1012
|
+
></ng-container>
|
|
1013
|
+
}
|
|
1014
|
+
</view>
|
|
1015
|
+
}
|
|
1016
|
+
@if (trailingSpacerStyle !== null) {
|
|
1017
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
1018
|
+
}
|
|
944
1019
|
}
|
|
945
|
-
}
|
|
946
1020
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
1021
|
+
@if (footerDir !== undefined) {
|
|
1022
|
+
<view>
|
|
1023
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
1024
|
+
</view>
|
|
1025
|
+
}
|
|
1026
|
+
</scroll-view>
|
|
1027
|
+
}
|
|
1028
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: SymbioteHostPropsDirective, selector: "[symbioteHostProps]", inputs: ["symbioteHostProps"], exportAs: ["symbioteHost"] }, { kind: "directive", type: VListOutletDirective, selector: "[vListOutlet]", inputs: ["vListOutlet", "vListOutletContext"] }, { kind: "component", type: ViewHost, selector: "view" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
954
1029
|
}
|
|
955
1030
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: VirtualizedList, decorators: [{
|
|
956
1031
|
type: Component,
|
|
957
1032
|
args: [{
|
|
958
1033
|
selector: 'VirtualizedList',
|
|
959
1034
|
standalone: true,
|
|
960
|
-
viewProviders: [provideGateDemand(() => VirtualizedList)],
|
|
961
1035
|
hostDirectives: [
|
|
962
1036
|
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
963
1037
|
],
|
|
964
1038
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
965
|
-
imports: [
|
|
1039
|
+
imports: [SymbioteHostPropsDirective, VListOutletDirective, ViewHost],
|
|
966
1040
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
967
1041
|
template: `
|
|
968
|
-
|
|
969
|
-
[
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
[accessibilityLabel]="foldedAccessibility().accessibilityLabel"
|
|
976
|
-
[accessibilityHint]="foldedAccessibility().accessibilityHint"
|
|
977
|
-
[accessibilityRole]="foldedAccessibility().accessibilityRole"
|
|
978
|
-
[accessibilityState]="foldedAccessibility().accessibilityState"
|
|
979
|
-
[accessibilityValue]="foldedAccessibility().accessibilityValue"
|
|
980
|
-
[accessibilityActions]="foldedAccessibility().accessibilityActions"
|
|
981
|
-
[accessibilityLabelledBy]="foldedAccessibility().accessibilityLabelledBy"
|
|
982
|
-
[importantForAccessibility]="
|
|
983
|
-
foldedAccessibility().importantForAccessibility
|
|
984
|
-
"
|
|
985
|
-
[accessibilityLiveRegion]="foldedAccessibility().accessibilityLiveRegion"
|
|
986
|
-
[screenReaderFocusable]="foldedAccessibility().screenReaderFocusable"
|
|
987
|
-
[accessibilityViewIsModal]="
|
|
988
|
-
foldedAccessibility().accessibilityViewIsModal
|
|
989
|
-
"
|
|
990
|
-
[accessibilityElementsHidden]="
|
|
991
|
-
foldedAccessibility().accessibilityElementsHidden
|
|
992
|
-
"
|
|
993
|
-
[accessibilityIgnoresInvertColors]="
|
|
994
|
-
foldedAccessibility().accessibilityIgnoresInvertColors
|
|
995
|
-
"
|
|
996
|
-
[accessibilityLanguage]="foldedAccessibility().accessibilityLanguage"
|
|
997
|
-
[accessibilityRespondsToUserInteraction]="
|
|
998
|
-
foldedAccessibility().accessibilityRespondsToUserInteraction
|
|
999
|
-
"
|
|
1000
|
-
[accessibilityShowsLargeContentViewer]="
|
|
1001
|
-
foldedAccessibility().accessibilityShowsLargeContentViewer
|
|
1002
|
-
"
|
|
1003
|
-
[accessibilityLargeContentTitle]="
|
|
1004
|
-
foldedAccessibility().accessibilityLargeContentTitle
|
|
1005
|
-
"
|
|
1006
|
-
(accessibilityAction)="accessibilityActionTick($event)"
|
|
1007
|
-
(accessibilityTap)="accessibilityTapTick($event)"
|
|
1008
|
-
(magicTap)="magicTapTick($event)"
|
|
1009
|
-
(accessibilityEscape)="accessibilityEscapeTick($event)"
|
|
1010
|
-
[onScroll]="onScrollTick"
|
|
1011
|
-
(layout)="onLayoutTick($event)"
|
|
1012
|
-
[onScrollBeginDrag]="onScrollBeginDrag"
|
|
1013
|
-
[onScrollEndDrag]="onScrollEndDrag"
|
|
1014
|
-
[onMomentumScrollBegin]="onMomentumScrollBegin"
|
|
1015
|
-
[onMomentumScrollEnd]="onMomentumScrollEnd"
|
|
1016
|
-
[scrollEventThrottle]="scrollEventThrottle"
|
|
1017
|
-
[keyboardShouldPersistTaps]="keyboardShouldPersistTaps"
|
|
1018
|
-
[keyboardDismissMode]="keyboardDismissMode"
|
|
1019
|
-
[contentOffset]="commandedOffset"
|
|
1020
|
-
[stickyHeaderIndices]="renderedStickyIndices"
|
|
1021
|
-
[maintainVisibleContentPosition]="resolvedMaintainVisibleContentPosition"
|
|
1022
|
-
>
|
|
1023
|
-
@if (shouldRenderRefreshControl) {
|
|
1024
|
-
<RefreshControl
|
|
1025
|
-
[refreshing]="refreshing ?? false"
|
|
1026
|
-
(refresh)="handleRefresh()"
|
|
1027
|
-
[progressViewOffset]="progressViewOffset"
|
|
1028
|
-
/>
|
|
1029
|
-
}
|
|
1042
|
+
@if (isHorizontal) {
|
|
1043
|
+
<horizontal-scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
1044
|
+
@if (shouldRenderRefreshControl) {
|
|
1045
|
+
<refresh-control
|
|
1046
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
1047
|
+
></refresh-control>
|
|
1048
|
+
}
|
|
1030
1049
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1050
|
+
@if (headerDir !== undefined) {
|
|
1051
|
+
<view>
|
|
1052
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
1053
|
+
</view>
|
|
1054
|
+
}
|
|
1036
1055
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1056
|
+
@if (itemCount === 0) {
|
|
1057
|
+
@if (emptyDir !== undefined) {
|
|
1058
|
+
<view>
|
|
1059
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
1060
|
+
</view>
|
|
1061
|
+
}
|
|
1062
|
+
} @else {
|
|
1063
|
+
@if (leadingSpacerStyle !== null) {
|
|
1064
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
1065
|
+
}
|
|
1066
|
+
@if (forcedStickyCell !== null) {
|
|
1067
|
+
<view
|
|
1068
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
1069
|
+
[style]="cellStyle"
|
|
1070
|
+
>
|
|
1071
|
+
<ng-container
|
|
1072
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1073
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
1074
|
+
></ng-container>
|
|
1075
|
+
</view>
|
|
1076
|
+
}
|
|
1077
|
+
@if (gapSpacerStyle !== null) {
|
|
1078
|
+
<view [style]="gapSpacerStyle"></view>
|
|
1079
|
+
}
|
|
1080
|
+
@for (cell of windowCells; track cell.key) {
|
|
1081
|
+
<view
|
|
1082
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
1083
|
+
[style]="cellStyle"
|
|
1084
|
+
>
|
|
1085
|
+
<ng-container
|
|
1086
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1087
|
+
[vListOutletContext]="cell.context"
|
|
1088
|
+
></ng-container>
|
|
1089
|
+
@if (cell.separatorContext !== undefined) {
|
|
1090
|
+
<ng-container
|
|
1091
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
1092
|
+
[vListOutletContext]="cell.separatorContext"
|
|
1093
|
+
></ng-container>
|
|
1094
|
+
}
|
|
1095
|
+
</view>
|
|
1096
|
+
}
|
|
1097
|
+
@if (trailingSpacerStyle !== null) {
|
|
1098
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
1099
|
+
}
|
|
1042
1100
|
}
|
|
1043
|
-
|
|
1044
|
-
@if (
|
|
1045
|
-
<
|
|
1101
|
+
|
|
1102
|
+
@if (footerDir !== undefined) {
|
|
1103
|
+
<view>
|
|
1104
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
1105
|
+
</view>
|
|
1046
1106
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
[vListOutletContext]="forcedStickyCell.context"
|
|
1055
|
-
></ng-container>
|
|
1056
|
-
</symbiote-view>
|
|
1107
|
+
</horizontal-scroll-view>
|
|
1108
|
+
} @else {
|
|
1109
|
+
<scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
1110
|
+
@if (shouldRenderRefreshControl) {
|
|
1111
|
+
<refresh-control
|
|
1112
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
1113
|
+
></refresh-control>
|
|
1057
1114
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1115
|
+
|
|
1116
|
+
@if (headerDir !== undefined) {
|
|
1117
|
+
<view>
|
|
1118
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
1119
|
+
</view>
|
|
1060
1120
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
@
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
<
|
|
1074
|
-
|
|
1075
|
-
[
|
|
1076
|
-
|
|
1077
|
-
@if (cell.separatorContext !== undefined) {
|
|
1121
|
+
|
|
1122
|
+
@if (itemCount === 0) {
|
|
1123
|
+
@if (emptyDir !== undefined) {
|
|
1124
|
+
<view>
|
|
1125
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
1126
|
+
</view>
|
|
1127
|
+
}
|
|
1128
|
+
} @else {
|
|
1129
|
+
@if (leadingSpacerStyle !== null) {
|
|
1130
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
1131
|
+
}
|
|
1132
|
+
@if (forcedStickyCell !== null) {
|
|
1133
|
+
<view
|
|
1134
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
1135
|
+
[style]="cellStyle"
|
|
1136
|
+
>
|
|
1078
1137
|
<ng-container
|
|
1079
|
-
[vListOutlet]="
|
|
1080
|
-
[vListOutletContext]="
|
|
1138
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1139
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
1081
1140
|
></ng-container>
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1141
|
+
</view>
|
|
1142
|
+
}
|
|
1143
|
+
@if (gapSpacerStyle !== null) {
|
|
1144
|
+
<view [style]="gapSpacerStyle"></view>
|
|
1145
|
+
}
|
|
1146
|
+
<!-- The separator sits INSIDE the measuring view, as RN's own cell renderer places it
|
|
1147
|
+
(VirtualizedListCellRenderer.js:218-221). As a sibling it is an extra flex child, so
|
|
1148
|
+
the chrome between two cells is gap + separator + gap while a spacer collapsing that
|
|
1149
|
+
region replaces it with a single gap — every cell below the leading spacer then lands
|
|
1150
|
+
short by (separator + gap) and the content jumps by that much whenever the window's
|
|
1151
|
+
first index moves. Measured at exactly 17px on device 2026-08-19; see
|
|
1152
|
+
.claude/rules/list-geometry-feedback-loop.md. -->
|
|
1153
|
+
@for (cell of windowCells; track cell.key) {
|
|
1154
|
+
<view
|
|
1155
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
1156
|
+
[style]="cellStyle"
|
|
1157
|
+
>
|
|
1158
|
+
<ng-container
|
|
1159
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1160
|
+
[vListOutletContext]="cell.context"
|
|
1161
|
+
></ng-container>
|
|
1162
|
+
@if (cell.separatorContext !== undefined) {
|
|
1163
|
+
<ng-container
|
|
1164
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
1165
|
+
[vListOutletContext]="cell.separatorContext"
|
|
1166
|
+
></ng-container>
|
|
1167
|
+
}
|
|
1168
|
+
</view>
|
|
1169
|
+
}
|
|
1170
|
+
@if (trailingSpacerStyle !== null) {
|
|
1171
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
1172
|
+
}
|
|
1087
1173
|
}
|
|
1088
|
-
}
|
|
1089
1174
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1175
|
+
@if (footerDir !== undefined) {
|
|
1176
|
+
<view>
|
|
1177
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
1178
|
+
</view>
|
|
1179
|
+
}
|
|
1180
|
+
</scroll-view>
|
|
1181
|
+
}
|
|
1096
1182
|
`,
|
|
1097
1183
|
}]
|
|
1098
1184
|
}], propDecorators: { endReached: [{
|
|
@@ -1265,8 +1351,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1265
1351
|
}], separatorDir: [{
|
|
1266
1352
|
type: ContentChild,
|
|
1267
1353
|
args: [VListSeparatorDirective]
|
|
1268
|
-
}],
|
|
1354
|
+
}], scrollHostRef: [{
|
|
1269
1355
|
type: ViewChild,
|
|
1270
|
-
args: [
|
|
1356
|
+
args: ['scrollHost', { read: ElementRef }]
|
|
1271
1357
|
}] } });
|
|
1272
1358
|
//# sourceMappingURL=index.js.map
|