@symbiote-native/angular 0.5.0 → 0.6.1
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/babel-register-composed.cjs +11 -3
- package/build/angular/anchor-host-registry.d.ts +2 -0
- package/build/angular/anchor-host-registry.js +84 -0
- package/build/angular/anchor-host-registry.js.map +1 -0
- package/build/angular/components/input-accessory-view/index.d.ts +2 -1
- package/build/angular/components/input-accessory-view/index.js +28 -39
- package/build/angular/components/input-accessory-view/index.js.map +1 -1
- package/build/angular/components/scroll-view/projection.js +70 -48
- package/build/angular/components/scroll-view/projection.js.map +1 -1
- package/build/angular/components/scroll-view/shared.js +28 -25
- package/build/angular/components/scroll-view/shared.js.map +1 -1
- package/build/angular/components/scroll-view/sticky-header.d.ts +6 -7
- package/build/angular/components/scroll-view/sticky-header.js +82 -65
- package/build/angular/components/scroll-view/sticky-header.js.map +1 -1
- package/build/angular/components/touchable/index.d.ts +2 -5
- package/build/angular/components/touchable/index.js +36 -56
- package/build/angular/components/touchable/index.js.map +1 -1
- package/build/angular/components/virtualized-list/index.d.ts +5 -20
- package/build/angular/components/virtualized-list/index.js +145 -275
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.js +2 -4
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/index.d.ts +2 -1
- package/build/angular/index.js +6 -1
- package/build/angular/index.js.map +1 -1
- package/build/angular/renderer/index.d.ts +0 -1
- package/build/angular/renderer/index.js +5 -60
- package/build/angular/renderer/index.js.map +1 -1
- package/package.json +9 -7
- package/src/__tests__/responder-nested-cd.test.ts +1 -1
- package/src/anchor-host-registry.ts +88 -0
- package/src/components/input-accessory-view/index.ts +30 -39
- package/src/components/scroll-view/projection.ts +76 -46
- package/src/components/scroll-view/shared.ts +27 -19
- package/src/components/scroll-view/sticky-header.ts +89 -64
- package/src/components/touchable/index.ts +52 -66
- package/src/components/virtualized-list/index.ts +157 -368
- package/src/components/virtualized-section-list/index.ts +6 -3
- package/src/index.ts +6 -1
- package/src/renderer/index.ts +5 -63
- package/src/renderer/renderer.test.ts +2 -1
- package/build/bootstrap.d.ts +0 -7
- package/build/bootstrap.js +0 -14
- package/build/components/activity-indicator/index.android.d.ts +0 -9
- package/build/components/activity-indicator/index.android.js +0 -64
- package/build/components/activity-indicator/index.d.ts +0 -1
- package/build/components/activity-indicator/index.ios.d.ts +0 -5
- package/build/components/activity-indicator/index.ios.js +0 -64
- package/build/components/activity-indicator/index.js +0 -3
- package/build/components/activity-indicator/shared.d.ts +0 -64
- package/build/components/activity-indicator/shared.js +0 -424
- package/build/components/button.d.ts +0 -60
- package/build/components/button.js +0 -456
- package/build/components/flat-list/index.d.ts +0 -119
- package/build/components/flat-list/index.js +0 -691
- package/build/components/image/index.android.d.ts +0 -7
- package/build/components/image/index.android.js +0 -88
- package/build/components/image/index.d.ts +0 -1
- package/build/components/image/index.ios.d.ts +0 -7
- package/build/components/image/index.ios.js +0 -88
- package/build/components/image/index.js +0 -2
- package/build/components/image/shared.d.ts +0 -101
- package/build/components/image/shared.js +0 -405
- package/build/components/image-background.d.ts +0 -78
- package/build/components/image-background.js +0 -583
- package/build/components/input-accessory-view/index.d.ts +0 -56
- package/build/components/input-accessory-view/index.js +0 -403
- package/build/components/keyboard-avoiding-view/index.d.ts +0 -79
- package/build/components/keyboard-avoiding-view/index.js +0 -504
- package/build/components/modal/index.d.ts +0 -89
- package/build/components/modal/index.js +0 -529
- package/build/components/pressable/index.d.ts +0 -120
- package/build/components/pressable/index.js +0 -639
- package/build/components/refresh-control/index.d.ts +0 -83
- package/build/components/refresh-control/index.js +0 -509
- package/build/components/safe-area-view/index.d.ts +0 -54
- package/build/components/safe-area-view/index.js +0 -388
- package/build/components/scroll-view/index.android.d.ts +0 -16
- package/build/components/scroll-view/index.android.js +0 -228
- package/build/components/scroll-view/index.d.ts +0 -3
- package/build/components/scroll-view/index.ios.d.ts +0 -6
- package/build/components/scroll-view/index.ios.js +0 -113
- package/build/components/scroll-view/index.js +0 -5
- package/build/components/scroll-view/projection.d.ts +0 -32
- package/build/components/scroll-view/projection.js +0 -311
- package/build/components/scroll-view/shared.d.ts +0 -225
- package/build/components/scroll-view/shared.js +0 -757
- package/build/components/scroll-view/sticky-header.d.ts +0 -38
- package/build/components/scroll-view/sticky-header.js +0 -257
- package/build/components/section-list/index.d.ts +0 -113
- package/build/components/section-list/index.js +0 -768
- package/build/components/switch/index.android.d.ts +0 -17
- package/build/components/switch/index.android.js +0 -107
- package/build/components/switch/index.d.ts +0 -1
- package/build/components/switch/index.ios.d.ts +0 -16
- package/build/components/switch/index.ios.js +0 -106
- package/build/components/switch/index.js +0 -3
- package/build/components/switch/shared.d.ts +0 -37
- package/build/components/switch/shared.js +0 -176
- package/build/components/text-input.d.ts +0 -150
- package/build/components/text-input.js +0 -821
- package/build/components/touchable/index.d.ts +0 -241
- package/build/components/touchable/index.js +0 -1672
- package/build/components/touchable-native-feedback/index.d.ts +0 -80
- package/build/components/touchable-native-feedback/index.js +0 -545
- package/build/components/virtualized-list/directives.d.ts +0 -41
- package/build/components/virtualized-list/directives.js +0 -249
- package/build/components/virtualized-list/index.d.ts +0 -265
- package/build/components/virtualized-list/index.js +0 -1343
- package/build/components/virtualized-section-list/directives.d.ts +0 -28
- package/build/components/virtualized-section-list/directives.js +0 -148
- package/build/components/virtualized-section-list/index.d.ts +0 -175
- package/build/components/virtualized-section-list/index.js +0 -854
- package/build/components.d.ts +0 -39
- package/build/components.js +0 -37
- package/build/create-portal/index.d.ts +0 -17
- package/build/create-portal/index.js +0 -128
- package/build/create-tunnel/index.d.ts +0 -29
- package/build/create-tunnel/index.js +0 -181
- package/build/descriptor-to-angular/index.d.ts +0 -19
- package/build/descriptor-to-angular/index.js +0 -186
- package/build/host-instance/index.d.ts +0 -2
- package/build/host-instance/index.js +0 -36
- package/build/index.d.ts +0 -21
- package/build/index.js +0 -40
- package/build/modules/animated/animated-leaf-binder/index.d.ts +0 -14
- package/build/modules/animated/animated-leaf-binder/index.js +0 -90
- package/build/modules/animated/animated-leaf-binder.d.ts +0 -14
- package/build/modules/animated/animated-leaf-binder.js +0 -90
- package/build/modules/animated/create-animated-component.d.ts +0 -48
- package/build/modules/animated/create-animated-component.js +0 -425
- package/build/modules/animated/index.d.ts +0 -51
- package/build/modules/animated/index.js +0 -58
- package/build/modules/app-registry/index.d.ts +0 -7
- package/build/modules/app-registry/index.js +0 -22
- package/build/modules/status-bar/index.d.ts +0 -17
- package/build/modules/status-bar/index.js +0 -121
- package/build/primitives/index.d.ts +0 -32
- package/build/primitives/index.js +0 -386
- package/build/primitives/shared.d.ts +0 -83
- package/build/primitives/shared.js +0 -230
- package/build/render/index.d.ts +0 -11
- package/build/render/index.js +0 -170
- package/build/renderer/index.d.ts +0 -38
- package/build/renderer/index.js +0 -355
- package/build/services/color-scheme.service.d.ts +0 -13
- package/build/services/color-scheme.service.js +0 -72
- package/build/services/index.d.ts +0 -2
- package/build/services/index.js +0 -2
- package/build/services/window-dimensions.service.d.ts +0 -13
- package/build/services/window-dimensions.service.js +0 -83
|
@@ -181,22 +181,12 @@ export declare class VirtualizedList<ItemT = unknown> implements IVirtualizedLis
|
|
|
181
181
|
x: number;
|
|
182
182
|
y: number;
|
|
183
183
|
} | undefined;
|
|
184
|
-
private
|
|
185
|
-
private
|
|
186
|
-
private measureVersion;
|
|
187
|
-
private metricsCore;
|
|
188
|
-
private readonly measured;
|
|
184
|
+
private readonly listState;
|
|
185
|
+
private renderVersion;
|
|
189
186
|
private readonly cellMeasures;
|
|
190
187
|
private readonly separatorOverrides;
|
|
191
|
-
private committedWindow;
|
|
192
|
-
private sentEndForContentLength;
|
|
193
|
-
private sentStartForContentLength;
|
|
194
|
-
private lastViewable;
|
|
195
188
|
private viewableTimer;
|
|
196
189
|
private batchTimer;
|
|
197
|
-
private hasInteracted;
|
|
198
|
-
private appliedInitialScroll;
|
|
199
|
-
private firstVisibleKey;
|
|
200
190
|
private lastEffectSignature;
|
|
201
191
|
private lastRecompute;
|
|
202
192
|
private readonly cdr;
|
|
@@ -219,10 +209,12 @@ export declare class VirtualizedList<ItemT = unknown> implements IVirtualizedLis
|
|
|
219
209
|
magicTapTick: (event: ISymbioteEvent) => void;
|
|
220
210
|
accessibilityEscapeTick: (event: ISymbioteEvent) => void;
|
|
221
211
|
private keyFor;
|
|
212
|
+
private buildInputs;
|
|
213
|
+
private dispatch;
|
|
214
|
+
private runEffects;
|
|
222
215
|
ngDoCheck(): void;
|
|
223
216
|
ngAfterViewChecked(): void;
|
|
224
217
|
ngOnDestroy(): void;
|
|
225
|
-
private recomputeMetrics;
|
|
226
218
|
private recomputeView;
|
|
227
219
|
private buildSeparatorContext;
|
|
228
220
|
private spacerStyle;
|
|
@@ -256,13 +248,6 @@ export declare class VirtualizedList<ItemT = unknown> implements IVirtualizedLis
|
|
|
256
248
|
getScrollNode(): ISymbioteNode | null;
|
|
257
249
|
recordInteraction(): void;
|
|
258
250
|
private scrollToPixel;
|
|
259
|
-
private offsetForIndexLocal;
|
|
260
|
-
private effectBatchFill;
|
|
261
|
-
private effectEndReached;
|
|
262
|
-
private effectStartReached;
|
|
263
|
-
private effectViewability;
|
|
264
|
-
private effectInitialScroll;
|
|
265
|
-
private effectMaintainVisibleContentPosition;
|
|
266
251
|
static ɵfac: i0.ɵɵFactoryDeclaration<VirtualizedList<any>, never>;
|
|
267
252
|
static ɵcmp: i0.ɵɵComponentDeclaration<VirtualizedList<any>, "VirtualizedList", never, { "data": { "alias": "data"; "required": true; }; "getItem": { "alias": "getItem"; "required": true; }; "getItemCount": { "alias": "getItemCount"; "required": true; }; "keyExtractor": { "alias": "keyExtractor"; "required": false; }; "getItemLayout": { "alias": "getItemLayout"; "required": false; }; "horizontal": { "alias": "horizontal"; "required": false; }; "inverted": { "alias": "inverted"; "required": false; }; "extraData": { "alias": "extraData"; "required": false; }; "onEndReachedThreshold": { "alias": "onEndReachedThreshold"; "required": false; }; "onStartReachedThreshold": { "alias": "onStartReachedThreshold"; "required": false; }; "refreshing": { "alias": "refreshing"; "required": false; }; "progressViewOffset": { "alias": "progressViewOffset"; "required": false; }; "refreshRequested": { "alias": "refreshRequested"; "required": false; }; "viewabilityConfig": { "alias": "viewabilityConfig"; "required": false; }; "viewabilityConfigCallbackPairs": { "alias": "viewabilityConfigCallbackPairs"; "required": false; }; "initialNumToRender": { "alias": "initialNumToRender"; "required": false; }; "initialScrollIndex": { "alias": "initialScrollIndex"; "required": false; }; "maxToRenderPerBatch": { "alias": "maxToRenderPerBatch"; "required": false; }; "updateCellsBatchingPeriod": { "alias": "updateCellsBatchingPeriod"; "required": false; }; "windowSize": { "alias": "windowSize"; "required": false; }; "stickyHeaderIndices": { "alias": "stickyHeaderIndices"; "required": false; }; "maintainVisibleContentPosition": { "alias": "maintainVisibleContentPosition"; "required": false; }; "onScroll": { "alias": "onScroll"; "required": false; }; "onScrollBeginDrag": { "alias": "onScrollBeginDrag"; "required": false; }; "onScrollEndDrag": { "alias": "onScrollEndDrag"; "required": false; }; "onMomentumScrollBegin": { "alias": "onMomentumScrollBegin"; "required": false; }; "onMomentumScrollEnd": { "alias": "onMomentumScrollEnd"; "required": false; }; "scrollEventThrottle": { "alias": "scrollEventThrottle"; "required": false; }; "keyboardShouldPersistTaps": { "alias": "keyboardShouldPersistTaps"; "required": false; }; "keyboardDismissMode": { "alias": "keyboardDismissMode"; "required": false; }; "style": { "alias": "style"; "required": false; }; "contentContainerStyle": { "alias": "contentContainerStyle"; "required": false; }; "testID": { "alias": "testID"; "required": false; }; "nativeID": { "alias": "nativeID"; "required": false; }; "accessible": { "alias": "accessible"; "required": false; }; "accessibilityLabel": { "alias": "accessibilityLabel"; "required": false; }; "accessibilityHint": { "alias": "accessibilityHint"; "required": false; }; "accessibilityRole": { "alias": "accessibilityRole"; "required": false; }; "accessibilityState": { "alias": "accessibilityState"; "required": false; }; "accessibilityValue": { "alias": "accessibilityValue"; "required": false; }; "accessibilityActions": { "alias": "accessibilityActions"; "required": false; }; "accessibilityLabelledBy": { "alias": "accessibilityLabelledBy"; "required": false; }; "importantForAccessibility": { "alias": "importantForAccessibility"; "required": false; }; "accessibilityLiveRegion": { "alias": "accessibilityLiveRegion"; "required": false; }; "screenReaderFocusable": { "alias": "screenReaderFocusable"; "required": false; }; "accessibilityViewIsModal": { "alias": "accessibilityViewIsModal"; "required": false; }; "accessibilityElementsHidden": { "alias": "accessibilityElementsHidden"; "required": false; }; "accessibilityIgnoresInvertColors": { "alias": "accessibilityIgnoresInvertColors"; "required": false; }; "accessibilityLanguage": { "alias": "accessibilityLanguage"; "required": false; }; "accessibilityRespondsToUserInteraction": { "alias": "accessibilityRespondsToUserInteraction"; "required": false; }; "accessibilityShowsLargeContentViewer": { "alias": "accessibilityShowsLargeContentViewer"; "required": false; }; "accessibilityLargeContentTitle": { "alias": "accessibilityLargeContentTitle"; "required": false; }; "role": { "alias": "role"; "required": false; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; }; "ariaLabelledBy": { "alias": "ariaLabelledBy"; "required": false; }; "ariaLive": { "alias": "ariaLive"; "required": false; }; "ariaHidden": { "alias": "ariaHidden"; "required": false; }; "ariaBusy": { "alias": "ariaBusy"; "required": false; }; "ariaChecked": { "alias": "ariaChecked"; "required": false; }; "ariaDisabled": { "alias": "ariaDisabled"; "required": false; }; "ariaExpanded": { "alias": "ariaExpanded"; "required": false; }; "ariaSelected": { "alias": "ariaSelected"; "required": false; }; "ariaModal": { "alias": "ariaModal"; "required": false; }; "ariaValueMax": { "alias": "ariaValueMax"; "required": false; }; "ariaValueMin": { "alias": "ariaValueMin"; "required": false; }; "ariaValueNow": { "alias": "ariaValueNow"; "required": false; }; "ariaValueText": { "alias": "ariaValueText"; "required": false; }; }, { "endReached": "endReached"; "startReached": "startReached"; "refresh": "refresh"; "viewableItemsChanged": "viewableItemsChanged"; "scrollToIndexFailed": "scrollToIndexFailed"; "accessibilityAction": "accessibilityAction"; "accessibilityTap": "accessibilityTap"; "magicTap": "magicTap"; "accessibilityEscape": "accessibilityEscape"; }, ["itemDir", "headerDir", "footerDir", "emptyDir", "separatorDir"], never, true, never>;
|
|
268
253
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// Lists have no Descriptor render fn — the cell content is the framework's own children.
|
|
25
25
|
// Cells/spacers are plain symbiote-view host elements.
|
|
26
26
|
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, Input, Output, ViewChild, inject, } 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,
|
|
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, createInitialListState, isSeparatorGapInRange, listEffectSignature, readLayoutLength, readScrollOffset, reduceList, resolveAccessibilityProps, resolveItemKey, } from '@symbiote-native/components';
|
|
28
28
|
import { dlog, flattenStyle, } from '@symbiote-native/engine';
|
|
29
29
|
import { ScrollView } from '../scroll-view';
|
|
30
30
|
import { RefreshControl } from '../refresh-control';
|
|
@@ -148,34 +148,18 @@ export class VirtualizedList {
|
|
|
148
148
|
// The offset we are imperatively driving native to before the scroll handle attaches (rides down
|
|
149
149
|
// as contentOffset). A fresh object identity each push so the commit re-applies a repeated value.
|
|
150
150
|
commandedOffset = undefined;
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
count: EMPTY_OFFSET,
|
|
158
|
-
offsets: [],
|
|
159
|
-
lengths: [],
|
|
160
|
-
total: EMPTY_OFFSET,
|
|
161
|
-
first: FIRST_INDEX,
|
|
162
|
-
last: -1,
|
|
163
|
-
target: { first: FIRST_INDEX, last: -1 },
|
|
164
|
-
averageLength: EMPTY_OFFSET,
|
|
165
|
-
};
|
|
166
|
-
measured = new Map();
|
|
151
|
+
// The one folded state cell (the Angular twin of React's stateRef): scroll offset, viewport,
|
|
152
|
+
// measured lengths, committed window, edge/viewability dedup, MVCP anchor — all in IListState,
|
|
153
|
+
// driven by the shared reduceList. `renderVersion` bumps whenever a transition changes render
|
|
154
|
+
// state, so ngDoCheck's recompute-dedup can detect it (listState is a plain object, not tracked).
|
|
155
|
+
listState = createInitialListState();
|
|
156
|
+
renderVersion = EMPTY_OFFSET;
|
|
167
157
|
cellMeasures = new Map();
|
|
168
158
|
separatorOverrides = new Map();
|
|
169
|
-
// The
|
|
170
|
-
|
|
171
|
-
sentEndForContentLength = NO_CONTENT_LENGTH_SENT;
|
|
172
|
-
sentStartForContentLength = NO_CONTENT_LENGTH_SENT;
|
|
173
|
-
lastViewable = new Map();
|
|
159
|
+
// The adapter-owned debounce (minimumViewTime) and incremental-fill timers; the reducer only
|
|
160
|
+
// hands back a delay.
|
|
174
161
|
viewableTimer = null;
|
|
175
162
|
batchTimer = null;
|
|
176
|
-
hasInteracted = false;
|
|
177
|
-
appliedInitialScroll = false;
|
|
178
|
-
firstVisibleKey = null;
|
|
179
163
|
// Dedupes the after-commit effects: they run only when the windowing signature changed.
|
|
180
164
|
lastEffectSignature = '';
|
|
181
165
|
// Dedupes ngDoCheck's own recompute. WITHOUT this, recomputeView() unconditionally rebuilds
|
|
@@ -269,22 +253,18 @@ export class VirtualizedList {
|
|
|
269
253
|
if (offset === undefined)
|
|
270
254
|
return;
|
|
271
255
|
dlog(`Angular VirtualizedList onScroll offset=${offset}`);
|
|
272
|
-
// First scroll is the interaction that ungates waitForInteraction configs.
|
|
273
|
-
this.hasInteracted = true;
|
|
274
|
-
this.scrollOffset = offset;
|
|
275
256
|
// A real native scroll supersedes any pending commanded offset.
|
|
276
257
|
this.commandedOffset = undefined;
|
|
258
|
+
this.dispatch({ kind: 'scroll', offset });
|
|
277
259
|
// Compose, don't clobber: internal windowing ran first, now the user's onScroll.
|
|
278
260
|
this.onScroll?.(event);
|
|
279
|
-
this.cdr.markForCheck();
|
|
280
261
|
};
|
|
281
262
|
onLayoutTick = (event) => {
|
|
282
263
|
const length = readLayoutLength(event, this.isHorizontal);
|
|
283
264
|
if (length === undefined)
|
|
284
265
|
return;
|
|
285
266
|
dlog(`Angular VirtualizedList onLayout viewport=${length}`);
|
|
286
|
-
this.
|
|
287
|
-
this.cdr.markForCheck();
|
|
267
|
+
this.dispatch({ kind: 'layout', length });
|
|
288
268
|
};
|
|
289
269
|
// RefreshControl's refresh and ScrollView's accessibility events are real @Output()s now, bound
|
|
290
270
|
// via (refresh)="handleRefresh()" / (accessibilityAction)="accessibilityActionTick($event)" etc.
|
|
@@ -306,13 +286,105 @@ export class VirtualizedList {
|
|
|
306
286
|
};
|
|
307
287
|
keyFor = (index) => {
|
|
308
288
|
const item = this.getItem(this.data, index);
|
|
309
|
-
return
|
|
289
|
+
return resolveItemKey(item, index, this.keyExtractor);
|
|
310
290
|
};
|
|
291
|
+
// The reducer inputs, folded off the @Input()s each call. The edge/viewability listeners map to
|
|
292
|
+
// `.observed` (Angular's "is anyone bound to this @Output()"), so the reducer emits only when the
|
|
293
|
+
// consumer listens — the same on-demand gating the prop-callback era had.
|
|
294
|
+
buildInputs() {
|
|
295
|
+
return {
|
|
296
|
+
data: this.data,
|
|
297
|
+
getItem: this.getItem,
|
|
298
|
+
getItemCount: this.getItemCount,
|
|
299
|
+
keyExtractor: this.keyExtractor,
|
|
300
|
+
getItemLayout: this.getItemLayout,
|
|
301
|
+
horizontal: this.isHorizontal,
|
|
302
|
+
windowSize: this.windowSizeValue,
|
|
303
|
+
initialNumToRender: this.initialNumToRenderValue,
|
|
304
|
+
maxToRenderPerBatch: this.maxToRenderPerBatchValue,
|
|
305
|
+
updateCellsBatchingPeriod: this.updateCellsBatchingPeriodValue,
|
|
306
|
+
onEndReachedThreshold: this.onEndReachedThresholdValue,
|
|
307
|
+
onStartReachedThreshold: this.onStartReachedThresholdValue,
|
|
308
|
+
onEndReachedActive: this.endReached.observed,
|
|
309
|
+
onStartReachedActive: this.startReached.observed,
|
|
310
|
+
viewabilityPairs: buildViewabilityPairs(this.viewableItemsChanged.observed
|
|
311
|
+
? (info) => this.viewableItemsChanged.emit(info)
|
|
312
|
+
: undefined, this.viewabilityConfig, this.viewabilityConfigCallbackPairs),
|
|
313
|
+
maintainVisibleContentPosition: this.maintainVisibleContentPosition,
|
|
314
|
+
initialScrollIndex: this.initialScrollIndex,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
// Map a native event / imperative call to an action, run the returned effects, and mark the view
|
|
318
|
+
// dirty when render state changed (the native callbacks fire outside Angular's own bindings).
|
|
319
|
+
dispatch(action) {
|
|
320
|
+
const inputs = this.buildInputs();
|
|
321
|
+
const result = reduceList(this.listState, action, inputs);
|
|
322
|
+
this.runEffects(result.effects, inputs);
|
|
323
|
+
if (result.changed) {
|
|
324
|
+
this.renderVersion += 1;
|
|
325
|
+
this.cdr.markForCheck();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
runEffects(effects, inputs) {
|
|
329
|
+
for (const effect of effects) {
|
|
330
|
+
switch (effect.kind) {
|
|
331
|
+
case 'scroll-to':
|
|
332
|
+
this.scrollToPixel(effect.offset, effect.animated);
|
|
333
|
+
break;
|
|
334
|
+
case 'fire-end-reached':
|
|
335
|
+
this.endReached.emit({ distanceFromEnd: effect.distanceFromEnd });
|
|
336
|
+
break;
|
|
337
|
+
case 'fire-start-reached':
|
|
338
|
+
this.startReached.emit({ distanceFromStart: effect.distanceFromStart });
|
|
339
|
+
break;
|
|
340
|
+
case 'fire-scroll-to-index-failed':
|
|
341
|
+
this.scrollToIndexFailed.emit({
|
|
342
|
+
index: effect.index,
|
|
343
|
+
highestMeasuredFrameIndex: effect.highestMeasuredFrameIndex,
|
|
344
|
+
averageItemLength: effect.averageItemLength,
|
|
345
|
+
});
|
|
346
|
+
break;
|
|
347
|
+
case 'schedule-refill': {
|
|
348
|
+
if (this.batchTimer !== null)
|
|
349
|
+
clearTimeout(this.batchTimer);
|
|
350
|
+
this.batchTimer = setTimeout(() => {
|
|
351
|
+
this.batchTimer = null;
|
|
352
|
+
this.dispatch({ kind: 'batch-tick' });
|
|
353
|
+
}, effect.delay);
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
case 'fire-viewable': {
|
|
357
|
+
const pairs = inputs.viewabilityPairs;
|
|
358
|
+
const info = effect.info;
|
|
359
|
+
const map = effect.map;
|
|
360
|
+
const fire = () => {
|
|
361
|
+
for (const pair of pairs)
|
|
362
|
+
pair.onViewableItemsChanged(info);
|
|
363
|
+
this.dispatch({ kind: 'viewable-fired', map });
|
|
364
|
+
};
|
|
365
|
+
if (this.viewableTimer !== null) {
|
|
366
|
+
clearTimeout(this.viewableTimer);
|
|
367
|
+
this.viewableTimer = null;
|
|
368
|
+
}
|
|
369
|
+
if (effect.delay > EMPTY_OFFSET) {
|
|
370
|
+
this.viewableTimer = setTimeout(() => {
|
|
371
|
+
this.viewableTimer = null;
|
|
372
|
+
fire();
|
|
373
|
+
}, effect.delay);
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
fire();
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
311
383
|
// Once-per-CD recompute, BEFORE the template bindings are read (so the freshly computed
|
|
312
384
|
// template-bound fields render this pass — computing them in ngAfterContentChecked instead would
|
|
313
|
-
// trip ExpressionChangedAfterItHasBeenChecked).
|
|
314
|
-
//
|
|
315
|
-
//
|
|
385
|
+
// trip ExpressionChangedAfterItHasBeenChecked). The dedup guard runs refresh-metrics (which owns
|
|
386
|
+
// the controlled committedWindow throttle) only when something relevant changed, so the window
|
|
387
|
+
// grows exactly one step per meaningful CD. The projected templates (ContentChild) are resolved
|
|
316
388
|
// from the second CD on; the first paint (count/viewport still 0) corrects on the layout-driven CD.
|
|
317
389
|
ngDoCheck() {
|
|
318
390
|
const recomputeInputs = [
|
|
@@ -329,9 +401,8 @@ export class VirtualizedList {
|
|
|
329
401
|
this.maintainVisibleContentPosition,
|
|
330
402
|
this.style,
|
|
331
403
|
this.contentContainerStyle,
|
|
332
|
-
|
|
333
|
-
this.
|
|
334
|
-
this.measureVersion,
|
|
404
|
+
// Folds scroll / layout / measure / batch-tick: dispatch bumps renderVersion on any change.
|
|
405
|
+
this.renderVersion,
|
|
335
406
|
this.headerDir !== undefined,
|
|
336
407
|
this.footerDir !== undefined,
|
|
337
408
|
this.emptyDir !== undefined,
|
|
@@ -344,21 +415,18 @@ export class VirtualizedList {
|
|
|
344
415
|
previous.every((value, index) => value === recomputeInputs[index])) {
|
|
345
416
|
return;
|
|
346
417
|
}
|
|
347
|
-
|
|
418
|
+
// The single derive-per-CD: recompute the window off the current state before the view reads it.
|
|
419
|
+
reduceList(this.listState, { kind: 'refresh-metrics' }, this.buildInputs());
|
|
348
420
|
this.recomputeView();
|
|
349
421
|
}
|
|
350
422
|
ngAfterViewChecked() {
|
|
351
|
-
const
|
|
352
|
-
const signature = `${this.scrollOffset}|${this.viewportLength}|${m.first}|${m.last}|${m.count}|${m.total}|${this.measureVersion}`;
|
|
423
|
+
const signature = listEffectSignature(this.listState);
|
|
353
424
|
if (signature === this.lastEffectSignature)
|
|
354
425
|
return;
|
|
355
426
|
this.lastEffectSignature = signature;
|
|
356
|
-
this.
|
|
357
|
-
this.
|
|
358
|
-
this.
|
|
359
|
-
this.effectViewability();
|
|
360
|
-
this.effectInitialScroll();
|
|
361
|
-
this.effectMaintainVisibleContentPosition();
|
|
427
|
+
const inputs = this.buildInputs();
|
|
428
|
+
const result = reduceList(this.listState, { kind: 'commit' }, inputs);
|
|
429
|
+
this.runEffects(result.effects, inputs);
|
|
362
430
|
}
|
|
363
431
|
ngOnDestroy() {
|
|
364
432
|
if (this.viewableTimer !== null)
|
|
@@ -366,38 +434,8 @@ export class VirtualizedList {
|
|
|
366
434
|
if (this.batchTimer !== null)
|
|
367
435
|
clearTimeout(this.batchTimer);
|
|
368
436
|
}
|
|
369
|
-
recomputeMetrics() {
|
|
370
|
-
const count = this.getItemCount(this.data);
|
|
371
|
-
const gil = this.getItemLayout;
|
|
372
|
-
const data = this.data;
|
|
373
|
-
const fixedLayout = gil !== undefined
|
|
374
|
-
? (index) => {
|
|
375
|
-
const layout = gil(data, index);
|
|
376
|
-
return { length: layout.length, offset: layout.offset };
|
|
377
|
-
}
|
|
378
|
-
: undefined;
|
|
379
|
-
const averageLength = fixedLayout !== undefined
|
|
380
|
-
? count > FIRST_INDEX
|
|
381
|
-
? fixedLayout(FIRST_INDEX).length
|
|
382
|
-
: EMPTY_OFFSET
|
|
383
|
-
: averageMeasuredLength(this.measured);
|
|
384
|
-
const { offsets, lengths, total } = buildOffsets(count, this.measured, fixedLayout, averageLength);
|
|
385
|
-
const target = computeWindow(count, offsets, lengths, this.scrollOffset, this.viewportLength, this.windowSizeValue, this.initialNumToRenderValue);
|
|
386
|
-
const throttled = throttleWindow(target, this.committedWindow, this.maxToRenderPerBatchValue);
|
|
387
|
-
this.committedWindow = throttled;
|
|
388
|
-
this.metricsCore = {
|
|
389
|
-
count,
|
|
390
|
-
offsets,
|
|
391
|
-
lengths,
|
|
392
|
-
total,
|
|
393
|
-
first: throttled.first,
|
|
394
|
-
last: throttled.last,
|
|
395
|
-
target,
|
|
396
|
-
averageLength,
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
437
|
recomputeView() {
|
|
400
|
-
const m = this.
|
|
438
|
+
const m = this.listState.metrics;
|
|
401
439
|
this.itemCount = m.count;
|
|
402
440
|
const hasHeader = this.headerDir !== undefined;
|
|
403
441
|
const hasSeparators = this.separatorDir !== undefined;
|
|
@@ -428,7 +466,7 @@ export class VirtualizedList {
|
|
|
428
466
|
this.leadingSpacerStyle = null;
|
|
429
467
|
this.trailingSpacerStyle = null;
|
|
430
468
|
this.renderedStickyIndices = undefined;
|
|
431
|
-
dlog(`Angular VirtualizedList empty (viewport=${this.viewportLength})`);
|
|
469
|
+
dlog(`Angular VirtualizedList empty (viewport=${this.listState.viewportLength})`);
|
|
432
470
|
return;
|
|
433
471
|
}
|
|
434
472
|
const plan = buildListPlan({
|
|
@@ -473,7 +511,7 @@ export class VirtualizedList {
|
|
|
473
511
|
}
|
|
474
512
|
this.windowCells = cells;
|
|
475
513
|
dlog(`Angular VirtualizedList window [${m.first}, ${m.last}] of ${m.count} ` +
|
|
476
|
-
`(offset=${this.scrollOffset}, viewport=${this.viewportLength}, rendered=${cells.length})`);
|
|
514
|
+
`(offset=${this.listState.scrollOffset}, viewport=${this.listState.viewportLength}, rendered=${cells.length})`);
|
|
477
515
|
}
|
|
478
516
|
buildSeparatorContext(index, item) {
|
|
479
517
|
const overrides = this.separatorOverrides.get(index);
|
|
@@ -503,17 +541,13 @@ export class VirtualizedList {
|
|
|
503
541
|
if (existing !== undefined)
|
|
504
542
|
return existing;
|
|
505
543
|
const measure = (event) => {
|
|
506
|
-
if (this.getItemLayout !== undefined)
|
|
507
|
-
return;
|
|
508
544
|
const length = readLayoutLength(event, this.isHorizontal);
|
|
509
545
|
if (length === undefined)
|
|
510
546
|
return;
|
|
511
|
-
if (this.measured.get(index) === length)
|
|
512
|
-
return;
|
|
513
|
-
this.measured.set(index, length);
|
|
514
547
|
dlog(`Angular VirtualizedList cell ${index} measured length=${length}`);
|
|
515
|
-
|
|
516
|
-
|
|
548
|
+
// The reducer guards a fixed getItemLayout and a repeated length; a no-op leaves state and the
|
|
549
|
+
// view untouched (dispatch marks for check only when something changed).
|
|
550
|
+
this.dispatch({ kind: 'measure', index, length });
|
|
517
551
|
};
|
|
518
552
|
this.cellMeasures.set(index, measure);
|
|
519
553
|
return measure;
|
|
@@ -528,8 +562,8 @@ export class VirtualizedList {
|
|
|
528
562
|
return typeof value === 'object' && value !== null && 'nativeEvent' in value;
|
|
529
563
|
}
|
|
530
564
|
mergeSeparator(gapIndex, patch) {
|
|
531
|
-
const count = this.
|
|
532
|
-
if (gapIndex
|
|
565
|
+
const count = this.listState.metrics.count;
|
|
566
|
+
if (!isSeparatorGapInRange(gapIndex, count))
|
|
533
567
|
return;
|
|
534
568
|
this.separatorOverrides.set(gapIndex, { ...this.separatorOverrides.get(gapIndex), ...patch });
|
|
535
569
|
this.cdr.markForCheck();
|
|
@@ -552,37 +586,34 @@ export class VirtualizedList {
|
|
|
552
586
|
};
|
|
553
587
|
}
|
|
554
588
|
// ---- imperative handle (the shared IVirtualizedListHandle surface) ----
|
|
589
|
+
// Each scroll resolves to an offset (or a scroll-to-index failure) inside the reducer, then rides
|
|
590
|
+
// the scroll-to effect through scrollToPixel.
|
|
555
591
|
scrollToOffset(params) {
|
|
556
|
-
this.
|
|
592
|
+
this.dispatch({
|
|
593
|
+
kind: 'scroll-to-offset',
|
|
594
|
+
offset: params.offset,
|
|
595
|
+
animated: params.animated ?? true,
|
|
596
|
+
});
|
|
557
597
|
}
|
|
558
598
|
scrollToIndex(params) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
index: params.index,
|
|
567
|
-
highestMeasuredFrameIndex: highestMeasuredIndex(this.measured),
|
|
568
|
-
averageItemLength: m.averageLength,
|
|
569
|
-
});
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
this.scrollToPixel(this.offsetForIndexLocal(params.index, params.viewPosition ?? FIRST_INDEX, params.viewOffset ?? EMPTY_OFFSET), params.animated ?? true);
|
|
599
|
+
this.dispatch({
|
|
600
|
+
kind: 'scroll-to-index',
|
|
601
|
+
index: params.index,
|
|
602
|
+
animated: params.animated ?? true,
|
|
603
|
+
viewPosition: params.viewPosition ?? FIRST_INDEX,
|
|
604
|
+
viewOffset: params.viewOffset ?? EMPTY_OFFSET,
|
|
605
|
+
});
|
|
573
606
|
}
|
|
574
607
|
scrollToItem(params) {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
582
|
-
dlog('Angular VirtualizedList scrollToItem: item not found');
|
|
608
|
+
this.dispatch({
|
|
609
|
+
kind: 'scroll-to-item',
|
|
610
|
+
item: params.item,
|
|
611
|
+
animated: params.animated ?? true,
|
|
612
|
+
viewPosition: params.viewPosition ?? FIRST_INDEX,
|
|
613
|
+
});
|
|
583
614
|
}
|
|
584
615
|
scrollToEnd(params) {
|
|
585
|
-
this.
|
|
616
|
+
this.dispatch({ kind: 'scroll-to-end', animated: params?.animated ?? true });
|
|
586
617
|
}
|
|
587
618
|
flashScrollIndicators() {
|
|
588
619
|
this.scrollView?.flashScrollIndicators();
|
|
@@ -600,7 +631,7 @@ export class VirtualizedList {
|
|
|
600
631
|
return this.scrollView?.getScrollNode() ?? null;
|
|
601
632
|
}
|
|
602
633
|
recordInteraction() {
|
|
603
|
-
this.
|
|
634
|
+
this.dispatch({ kind: 'record-interaction' });
|
|
604
635
|
}
|
|
605
636
|
scrollToPixel(offset, animated) {
|
|
606
637
|
const clamped = Math.max(EMPTY_OFFSET, offset);
|
|
@@ -616,167 +647,6 @@ export class VirtualizedList {
|
|
|
616
647
|
this.commandedOffset = target;
|
|
617
648
|
this.cdr.markForCheck();
|
|
618
649
|
}
|
|
619
|
-
offsetForIndexLocal(index, viewPosition, viewOffset) {
|
|
620
|
-
const m = this.metricsCore;
|
|
621
|
-
return offsetForIndex(index, viewPosition, viewOffset, m.count, m.offsets, m.lengths, this.viewportLength);
|
|
622
|
-
}
|
|
623
|
-
// ---- after-commit effects (run in ngAfterViewChecked, the post-render seam) ----
|
|
624
|
-
// Batch fill: when the throttled window has not reached the target, schedule a re-render so the
|
|
625
|
-
// window keeps filling toward target over successive ticks (RN's incremental fill).
|
|
626
|
-
effectBatchFill() {
|
|
627
|
-
const m = this.metricsCore;
|
|
628
|
-
if (this.batchTimer !== null) {
|
|
629
|
-
clearTimeout(this.batchTimer);
|
|
630
|
-
this.batchTimer = null;
|
|
631
|
-
}
|
|
632
|
-
if (m.first <= m.target.first && m.last >= m.target.last)
|
|
633
|
-
return;
|
|
634
|
-
this.batchTimer = setTimeout(() => {
|
|
635
|
-
this.batchTimer = null;
|
|
636
|
-
this.measureVersion += 1;
|
|
637
|
-
this.cdr.markForCheck();
|
|
638
|
-
}, this.updateCellsBatchingPeriodValue);
|
|
639
|
-
}
|
|
640
|
-
// onEndReached: fire only when the actual last cell is rendered AND within threshold; dedup by
|
|
641
|
-
// content length; re-arm on scroll away from the end (RN _maybeCallOnEdgeReached).
|
|
642
|
-
effectEndReached() {
|
|
643
|
-
if (!this.endReached.observed || this.viewportLength <= EMPTY_OFFSET)
|
|
644
|
-
return;
|
|
645
|
-
const m = this.metricsCore;
|
|
646
|
-
const { distanceFromEnd, withinThreshold } = computeEndReached(m.total, this.scrollOffset, this.viewportLength, this.onEndReachedThresholdValue);
|
|
647
|
-
const lastCellRendered = m.last === m.count - 1;
|
|
648
|
-
if (withinThreshold && lastCellRendered && this.sentEndForContentLength !== m.total) {
|
|
649
|
-
this.sentEndForContentLength = m.total;
|
|
650
|
-
dlog(`Angular VirtualizedList endReached distanceFromEnd=${distanceFromEnd} ` +
|
|
651
|
-
`(last=${m.last} of ${m.count}, contentLength=${m.total})`);
|
|
652
|
-
this.endReached.emit({ distanceFromEnd });
|
|
653
|
-
}
|
|
654
|
-
if (!withinThreshold)
|
|
655
|
-
this.sentEndForContentLength = NO_CONTENT_LENGTH_SENT;
|
|
656
|
-
}
|
|
657
|
-
// startReached: the top-edge twin of endReached.
|
|
658
|
-
effectStartReached() {
|
|
659
|
-
if (!this.startReached.observed || this.viewportLength <= EMPTY_OFFSET)
|
|
660
|
-
return;
|
|
661
|
-
const m = this.metricsCore;
|
|
662
|
-
const { distanceFromStart, withinThreshold } = computeStartReached(this.scrollOffset, this.viewportLength, this.onStartReachedThresholdValue);
|
|
663
|
-
const firstCellRendered = m.first === FIRST_INDEX;
|
|
664
|
-
if (withinThreshold && firstCellRendered && this.sentStartForContentLength !== m.total) {
|
|
665
|
-
this.sentStartForContentLength = m.total;
|
|
666
|
-
dlog(`Angular VirtualizedList startReached distanceFromStart=${distanceFromStart} ` +
|
|
667
|
-
`(first=${m.first}, contentLength=${m.total})`);
|
|
668
|
-
this.startReached.emit({ distanceFromStart });
|
|
669
|
-
}
|
|
670
|
-
if (!withinThreshold)
|
|
671
|
-
this.sentStartForContentLength = NO_CONTENT_LENGTH_SENT;
|
|
672
|
-
}
|
|
673
|
-
// Viewability: recompute which rendered cells clear the threshold and, if the viewable set
|
|
674
|
-
// changed, fire viewableItemsChanged + every config/callback pair, honoring minimumViewTime.
|
|
675
|
-
effectViewability() {
|
|
676
|
-
const m = this.metricsCore;
|
|
677
|
-
const pairs = buildViewabilityPairs(this.viewableItemsChanged.observed
|
|
678
|
-
? (info) => this.viewableItemsChanged.emit(info)
|
|
679
|
-
: undefined, this.viewabilityConfig, this.viewabilityConfigCallbackPairs);
|
|
680
|
-
if (pairs.length === EMPTY_OFFSET || this.viewportLength <= EMPTY_OFFSET)
|
|
681
|
-
return;
|
|
682
|
-
if (m.count === FIRST_INDEX)
|
|
683
|
-
return;
|
|
684
|
-
const { tokens, map } = computeViewableSet({
|
|
685
|
-
first: m.first,
|
|
686
|
-
last: m.last,
|
|
687
|
-
count: m.count,
|
|
688
|
-
offsets: m.offsets,
|
|
689
|
-
lengths: m.lengths,
|
|
690
|
-
scrollOffset: this.scrollOffset,
|
|
691
|
-
viewportLength: this.viewportLength,
|
|
692
|
-
data: this.data,
|
|
693
|
-
getItem: this.getItem,
|
|
694
|
-
keyExtractor: this.keyExtractor,
|
|
695
|
-
pairs,
|
|
696
|
-
hasInteracted: this.hasInteracted,
|
|
697
|
-
});
|
|
698
|
-
const diff = diffViewable(this.lastViewable, map, tokens);
|
|
699
|
-
if (!diff.hasChanged)
|
|
700
|
-
return;
|
|
701
|
-
const commitAndFire = () => {
|
|
702
|
-
this.lastViewable = map;
|
|
703
|
-
dlog(`Angular VirtualizedList viewable=${tokens.length} changed=${diff.changed.length} ` +
|
|
704
|
-
`(window [${m.first}, ${m.last}])`);
|
|
705
|
-
const info = {
|
|
706
|
-
viewableItems: tokens,
|
|
707
|
-
changed: diff.changed,
|
|
708
|
-
};
|
|
709
|
-
for (const pair of pairs)
|
|
710
|
-
pair.onViewableItemsChanged(info);
|
|
711
|
-
};
|
|
712
|
-
const minimumViewTime = maxMinimumViewTime(pairs);
|
|
713
|
-
if (this.viewableTimer !== null) {
|
|
714
|
-
clearTimeout(this.viewableTimer);
|
|
715
|
-
this.viewableTimer = null;
|
|
716
|
-
}
|
|
717
|
-
if (minimumViewTime > EMPTY_OFFSET) {
|
|
718
|
-
dlog(`Angular VirtualizedList viewability debounce ${minimumViewTime}ms (window [${m.first}, ${m.last}])`);
|
|
719
|
-
this.viewableTimer = setTimeout(() => {
|
|
720
|
-
this.viewableTimer = null;
|
|
721
|
-
commitAndFire();
|
|
722
|
-
}, minimumViewTime);
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
commitAndFire();
|
|
726
|
-
}
|
|
727
|
-
// initialScrollIndex: once the first viewport is known, jump to that index a single time.
|
|
728
|
-
effectInitialScroll() {
|
|
729
|
-
if (this.initialScrollIndex === undefined || this.appliedInitialScroll)
|
|
730
|
-
return;
|
|
731
|
-
if (this.viewportLength <= EMPTY_OFFSET || this.metricsCore.count === FIRST_INDEX)
|
|
732
|
-
return;
|
|
733
|
-
this.appliedInitialScroll = true;
|
|
734
|
-
// The initial jump is instant (RN does not animate initialScrollIndex).
|
|
735
|
-
this.scrollToPixel(this.offsetForIndexLocal(this.initialScrollIndex, FIRST_INDEX, EMPTY_OFFSET), false);
|
|
736
|
-
}
|
|
737
|
-
// maintainVisibleContentPosition JS anchor adjustment: track the key of the item at
|
|
738
|
-
// minIndexForVisible; when a prepend moves it down, add the inserted extent in the leading SPACER
|
|
739
|
-
// (the off-window items native MVCP cannot see) to scrollOffset so the anchored item does not jump
|
|
740
|
-
// (RN getDerivedStateFromProps).
|
|
741
|
-
effectMaintainVisibleContentPosition() {
|
|
742
|
-
const m = this.metricsCore;
|
|
743
|
-
if (this.maintainVisibleContentPosition === undefined || m.count === FIRST_INDEX) {
|
|
744
|
-
this.firstVisibleKey = null;
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
|
-
const minIndexForVisible = this.maintainVisibleContentPosition.minIndexForVisible;
|
|
748
|
-
const newFirstVisibleKey = m.count > minIndexForVisible ? this.keyFor(minIndexForVisible) : null;
|
|
749
|
-
const prevKey = this.firstVisibleKey;
|
|
750
|
-
if (prevKey !== null && newFirstVisibleKey !== null && prevKey !== newFirstVisibleKey) {
|
|
751
|
-
let anchorIndex = -1;
|
|
752
|
-
for (let index = minIndexForVisible; index < m.count; index += 1) {
|
|
753
|
-
if (this.keyFor(index) === prevKey) {
|
|
754
|
-
anchorIndex = index;
|
|
755
|
-
break;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
if (anchorIndex > minIndexForVisible) {
|
|
759
|
-
const spacerEnd = Math.min(anchorIndex, this.committedWindow.first);
|
|
760
|
-
const insertedExtent = spacerEnd > minIndexForVisible
|
|
761
|
-
? m.offsets[spacerEnd] - m.offsets[minIndexForVisible]
|
|
762
|
-
: EMPTY_OFFSET;
|
|
763
|
-
if (insertedExtent > EMPTY_OFFSET) {
|
|
764
|
-
const autoThreshold = this.maintainVisibleContentPosition.autoscrollToTopThreshold;
|
|
765
|
-
const anchoredNearTop = autoThreshold !== undefined && this.scrollOffset <= autoThreshold;
|
|
766
|
-
if (anchoredNearTop) {
|
|
767
|
-
dlog(`Angular VirtualizedList MVCP autoscroll-to-top (offset=${this.scrollOffset} <= ${autoThreshold})`);
|
|
768
|
-
this.scrollToPixel(EMPTY_OFFSET, true);
|
|
769
|
-
}
|
|
770
|
-
else {
|
|
771
|
-
dlog(`Angular VirtualizedList MVCP adjust +${insertedExtent}px ` +
|
|
772
|
-
`(anchor "${prevKey}" moved ${minIndexForVisible}->${anchorIndex})`);
|
|
773
|
-
this.scrollToPixel(this.scrollOffset + insertedExtent, false);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
this.firstVisibleKey = newFirstVisibleKey;
|
|
779
|
-
}
|
|
780
650
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: VirtualizedList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
781
651
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", 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: "scrollView", first: true, predicate: ScrollView, descendants: true }], ngImport: i0, template: `
|
|
782
652
|
<ScrollView
|