@react-stately/virtualizer 3.7.2-nightly.4649 → 3.7.2-nightly.4656
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/dist/Layout.main.js +2 -41
- package/dist/Layout.main.js.map +1 -1
- package/dist/Layout.mjs +2 -41
- package/dist/Layout.module.js +2 -41
- package/dist/Layout.module.js.map +1 -1
- package/dist/LayoutInfo.main.js.map +1 -1
- package/dist/LayoutInfo.module.js.map +1 -1
- package/dist/OverscanManager.main.js +8 -43
- package/dist/OverscanManager.main.js.map +1 -1
- package/dist/OverscanManager.mjs +8 -43
- package/dist/OverscanManager.module.js +8 -43
- package/dist/OverscanManager.module.js.map +1 -1
- package/dist/ReusableView.main.js +23 -0
- package/dist/ReusableView.main.js.map +1 -1
- package/dist/ReusableView.mjs +23 -0
- package/dist/ReusableView.module.js +23 -0
- package/dist/ReusableView.module.js.map +1 -1
- package/dist/Virtualizer.main.js +123 -710
- package/dist/Virtualizer.main.js.map +1 -1
- package/dist/Virtualizer.mjs +124 -711
- package/dist/Virtualizer.module.js +124 -711
- package/dist/Virtualizer.module.js.map +1 -1
- package/dist/types.d.ts +64 -225
- package/dist/types.d.ts.map +1 -1
- package/dist/useVirtualizerState.main.js +39 -40
- package/dist/useVirtualizerState.main.js.map +1 -1
- package/dist/useVirtualizerState.mjs +40 -41
- package/dist/useVirtualizerState.module.js +40 -41
- package/dist/useVirtualizerState.module.js.map +1 -1
- package/dist/utils.main.js +1 -27
- package/dist/utils.main.js.map +1 -1
- package/dist/utils.mjs +2 -26
- package/dist/utils.module.js +2 -26
- package/dist/utils.module.js.map +1 -1
- package/package.json +4 -4
- package/src/Layout.ts +10 -55
- package/src/LayoutInfo.ts +2 -2
- package/src/OverscanManager.ts +10 -47
- package/src/ReusableView.ts +36 -7
- package/src/Virtualizer.ts +163 -1058
- package/src/types.ts +16 -38
- package/src/useVirtualizerState.ts +40 -39
- package/src/utils.ts +0 -52
- package/dist/Transaction.main.js +0 -32
- package/dist/Transaction.main.js.map +0 -1
- package/dist/Transaction.mjs +0 -27
- package/dist/Transaction.module.js +0 -27
- package/dist/Transaction.module.js.map +0 -1
- package/dist/tween.main.js +0 -67
- package/dist/tween.main.js.map +0 -1
- package/dist/tween.mjs +0 -61
- package/dist/tween.module.js +0 -61
- package/dist/tween.module.js.map +0 -1
- package/src/Transaction.ts +0 -28
- package/src/tween.ts +0 -83
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {concatIterators as $fc36f9a046a9ce79$export$cfc14088dfefce5f, difference as $fc36f9a046a9ce79$export$acaf96a27438246b, isSetEqual as $fc36f9a046a9ce79$export$a8d0d0c8d1c5df64} from "./utils.module.js";
|
|
1
|
+
import {isSetEqual as $fc36f9a046a9ce79$export$a8d0d0c8d1c5df64} from "./utils.module.js";
|
|
3
2
|
import {OverscanManager as $364191b3decf3697$export$4455ee6afb38dcbb} from "./OverscanManager.module.js";
|
|
4
|
-
import {Point as $3041db3296945e6e$export$baf26146a414f24a} from "./Point.module.js";
|
|
5
3
|
import {Rect as $60423f92c7f9ad87$export$c79fc6492f3af13d} from "./Rect.module.js";
|
|
6
4
|
import {ReusableView as $ad1d98aa8f0c31b4$export$1a5223887c560441} from "./ReusableView.module.js";
|
|
7
5
|
import {Size as $ee1bfa90a957fb8a$export$cb6da89c6af1a8ec} from "./Size.module.js";
|
|
8
|
-
import {Transaction as $8e135e531d8dcb66$export$febc5573c75cefb0} from "./Transaction.module.js";
|
|
9
6
|
|
|
10
7
|
/*
|
|
11
8
|
* Copyright 2020 Adobe. All rights reserved.
|
|
@@ -22,92 +19,12 @@ import {Transaction as $8e135e531d8dcb66$export$febc5573c75cefb0} from "./Transa
|
|
|
22
19
|
|
|
23
20
|
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
22
|
class $38b9490c1cca8fc4$export$89be5a243e59c4b2 {
|
|
29
|
-
_setContentSize(size) {
|
|
30
|
-
this._contentSize = size;
|
|
31
|
-
this.delegate.setContentSize(size);
|
|
32
|
-
}
|
|
33
|
-
_setContentOffset(offset) {
|
|
34
|
-
let rect = new (0, $60423f92c7f9ad87$export$c79fc6492f3af13d)(offset.x, offset.y, this._visibleRect.width, this._visibleRect.height);
|
|
35
|
-
this.delegate.setVisibleRect(rect);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Get the size of the scrollable content.
|
|
39
|
-
*/ get contentSize() {
|
|
40
|
-
return this._contentSize;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Get the collection view's currently visible rectangle.
|
|
44
|
-
*/ get visibleRect() {
|
|
45
|
-
return this._visibleRect;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Set the collection view's currently visible rectangle.
|
|
49
|
-
*/ set visibleRect(rect) {
|
|
50
|
-
this._setVisibleRect(rect);
|
|
51
|
-
}
|
|
52
|
-
_setVisibleRect(rect, forceUpdate = false) {
|
|
53
|
-
let current = this._visibleRect;
|
|
54
|
-
// Ignore if the rects are equal
|
|
55
|
-
if (rect.equals(current)) return;
|
|
56
|
-
if (this.shouldOverscan) this._overscanManager.setVisibleRect(rect);
|
|
57
|
-
let shouldInvalidate = this.layout && this.layout.shouldInvalidate(rect, this._visibleRect);
|
|
58
|
-
this._resetAnimatedContentOffset();
|
|
59
|
-
this._visibleRect = rect;
|
|
60
|
-
if (shouldInvalidate) // We are already in a layout effect when this method is called, so relayoutNow is appropriate.
|
|
61
|
-
this.relayoutNow({
|
|
62
|
-
offsetChanged: !rect.pointEquals(current),
|
|
63
|
-
sizeChanged: !rect.sizeEquals(current)
|
|
64
|
-
});
|
|
65
|
-
else this.updateSubviews(forceUpdate);
|
|
66
|
-
}
|
|
67
|
-
get collection() {
|
|
68
|
-
return this._collection;
|
|
69
|
-
}
|
|
70
|
-
set collection(data) {
|
|
71
|
-
this._setData(data);
|
|
72
|
-
}
|
|
73
|
-
_setData(data) {
|
|
74
|
-
if (data === this._collection) return;
|
|
75
|
-
if (this._collection) this._runTransaction(()=>{
|
|
76
|
-
this._collection = data;
|
|
77
|
-
}, this.transitionDuration > 0);
|
|
78
|
-
else {
|
|
79
|
-
this._collection = data;
|
|
80
|
-
this.reloadData();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Reloads the data from the data source and relayouts the collection view.
|
|
85
|
-
* Does not animate any changes. Equivalent to re-assigning the same data source
|
|
86
|
-
* to the collection view.
|
|
87
|
-
*/ reloadData() {
|
|
88
|
-
this.relayout({
|
|
89
|
-
contentChanged: true
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Returns the item with the given key.
|
|
94
|
-
*/ getItem(key) {
|
|
95
|
-
return this._collection ? this._collection.getItem(key) : null;
|
|
96
|
-
}
|
|
97
|
-
/** The set of persisted keys are always present in the DOM, even if not currently in view. */ get persistedKeys() {
|
|
98
|
-
return this._persistedKeys;
|
|
99
|
-
}
|
|
100
|
-
/** The set of persisted keys are always present in the DOM, even if not currently in view. */ set persistedKeys(persistedKeys) {
|
|
101
|
-
if (!(0, $fc36f9a046a9ce79$export$a8d0d0c8d1c5df64)(persistedKeys, this._persistedKeys)) {
|
|
102
|
-
this._persistedKeys = persistedKeys;
|
|
103
|
-
this.updateSubviews();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
23
|
/** Returns whether the given key, or an ancestor, is persisted. */ isPersistedKey(key) {
|
|
107
24
|
// Quick check if the key is directly in the set of persisted keys.
|
|
108
|
-
if (this.
|
|
25
|
+
if (this.persistedKeys.has(key)) return true;
|
|
109
26
|
// If not, check if the key is an ancestor of any of the persisted keys.
|
|
110
|
-
for (let k of this.
|
|
27
|
+
for (let k of this.persistedKeys)while(k != null){
|
|
111
28
|
let layoutInfo = this.layout.getLayoutInfo(k);
|
|
112
29
|
if (!layoutInfo) break;
|
|
113
30
|
k = layoutInfo.parentKey;
|
|
@@ -115,72 +32,16 @@ class $38b9490c1cca8fc4$export$89be5a243e59c4b2 {
|
|
|
115
32
|
}
|
|
116
33
|
return false;
|
|
117
34
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Get the collection view's layout.
|
|
120
|
-
*/ get layout() {
|
|
121
|
-
return this._layout;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Set the collection view's layout.
|
|
125
|
-
*/ set layout(layout) {
|
|
126
|
-
this.setLayout(layout);
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Sets the collection view's layout, optionally with an animated transition
|
|
130
|
-
* from the current layout to the new layout.
|
|
131
|
-
* @param layout The layout to switch to.
|
|
132
|
-
* @param animated Whether to animate the layout change.
|
|
133
|
-
*/ setLayout(layout, animated = false) {
|
|
134
|
-
if (layout === this._layout) return;
|
|
135
|
-
let applyLayout = ()=>{
|
|
136
|
-
if (this._layout) // @ts-ignore
|
|
137
|
-
this._layout.virtualizer = null;
|
|
138
|
-
layout.virtualizer = this;
|
|
139
|
-
this._layout = layout;
|
|
140
|
-
};
|
|
141
|
-
if (animated) // Animated layout transitions are really simple, thanks to our transaction support.
|
|
142
|
-
// We just set the layout inside a transaction action, which runs after the initial
|
|
143
|
-
// layout infos for the animation are retrieved from the previous layout. Then, the
|
|
144
|
-
// final layout infos are retrieved from the new layout, and animations occur.
|
|
145
|
-
this._runTransaction(applyLayout);
|
|
146
|
-
else {
|
|
147
|
-
applyLayout();
|
|
148
|
-
this.relayout();
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
_getReuseType(layoutInfo, content) {
|
|
152
|
-
if (layoutInfo.type === 'item' && content) {
|
|
153
|
-
let type = this.delegate.getType ? this.delegate.getType(content) : 'item';
|
|
154
|
-
let reuseType = type === 'item' ? 'item' : layoutInfo.type + '_' + type;
|
|
155
|
-
return {
|
|
156
|
-
type: type,
|
|
157
|
-
reuseType: reuseType
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
return {
|
|
161
|
-
type: layoutInfo.type,
|
|
162
|
-
reuseType: layoutInfo.type
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
35
|
getReusableView(layoutInfo) {
|
|
166
|
-
let
|
|
167
|
-
let
|
|
168
|
-
if (!this._reusableViews[reuseType]) this._reusableViews[reuseType] = [];
|
|
169
|
-
let reusable = this._reusableViews[reuseType];
|
|
170
|
-
let view = reusable.length > 0 ? reusable.pop() : new (0, $ad1d98aa8f0c31b4$export$1a5223887c560441)(this);
|
|
171
|
-
view.viewType = reuseType;
|
|
172
|
-
if (!this._animatedContentOffset.isOrigin()) {
|
|
173
|
-
layoutInfo = layoutInfo.copy();
|
|
174
|
-
layoutInfo.rect.x += this._animatedContentOffset.x;
|
|
175
|
-
layoutInfo.rect.y += this._animatedContentOffset.y;
|
|
176
|
-
}
|
|
36
|
+
let parentView = layoutInfo.parentKey != null ? this._visibleViews.get(layoutInfo.parentKey) : this._rootView;
|
|
37
|
+
let view = parentView.getReusableView(layoutInfo.type);
|
|
177
38
|
view.layoutInfo = layoutInfo;
|
|
178
39
|
this._renderView(view);
|
|
179
40
|
return view;
|
|
180
41
|
}
|
|
181
42
|
_renderView(reusableView) {
|
|
182
43
|
let { type: type, key: key } = reusableView.layoutInfo;
|
|
183
|
-
reusableView.content = this.getItem(key);
|
|
44
|
+
reusableView.content = this.collection.getItem(key);
|
|
184
45
|
reusableView.rendered = this._renderContent(type, reusableView.content);
|
|
185
46
|
}
|
|
186
47
|
_renderContent(type, content) {
|
|
@@ -191,33 +52,6 @@ class $38b9490c1cca8fc4$export$89be5a243e59c4b2 {
|
|
|
191
52
|
return rendered;
|
|
192
53
|
}
|
|
193
54
|
/**
|
|
194
|
-
* Returns an array of all currently visible views, including both
|
|
195
|
-
* item views and supplementary views.
|
|
196
|
-
*/ get visibleViews() {
|
|
197
|
-
return Array.from(this._visibleViews.values());
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Gets the visible view for the given type and key. Returns null if
|
|
201
|
-
* the view is not currently visible.
|
|
202
|
-
*
|
|
203
|
-
* @param key The key of the view to retrieve.
|
|
204
|
-
*/ getView(key) {
|
|
205
|
-
return this._visibleViews.get(key) || null;
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Returns an array of visible views matching the given type.
|
|
209
|
-
* @param type The view type to find.
|
|
210
|
-
*/ getViewsOfType(type) {
|
|
211
|
-
return this.visibleViews.filter((v)=>v.layoutInfo && v.layoutInfo.type === type);
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Returns the key for the given view. Returns null
|
|
215
|
-
* if the view is not currently visible.
|
|
216
|
-
*/ keyForView(view) {
|
|
217
|
-
if (view && view.layoutInfo) return view.layoutInfo.key;
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
55
|
* Returns the key for the item view currently at the given point.
|
|
222
56
|
*/ keyAtPoint(point) {
|
|
223
57
|
let rect = new (0, $60423f92c7f9ad87$export$c79fc6492f3af13d)(point.x, point.y, 1, 1);
|
|
@@ -229,581 +63,160 @@ class $38b9490c1cca8fc4$export$89be5a243e59c4b2 {
|
|
|
229
63
|
}
|
|
230
64
|
return null;
|
|
231
65
|
}
|
|
232
|
-
|
|
233
|
-
* Cleanup for when the Virtualizer will be unmounted.
|
|
234
|
-
*/ willUnmount() {
|
|
235
|
-
cancelAnimationFrame(this._relayoutRaf);
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Triggers a layout invalidation, and updates the visible subviews.
|
|
239
|
-
*/ relayout(context = {}) {
|
|
240
|
-
// Ignore relayouts while animating the scroll position
|
|
241
|
-
if (this._scrollAnimation || typeof requestAnimationFrame === 'undefined') return;
|
|
242
|
-
// If we already scheduled a relayout, extend the invalidation
|
|
243
|
-
// context so we coalesce multiple relayouts in the same frame.
|
|
244
|
-
if (this._invalidationContext) {
|
|
245
|
-
Object.assign(this._invalidationContext, context);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
this._invalidationContext = context;
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Performs a relayout immediately. Prefer {@link relayout} over this method
|
|
252
|
-
* where possible, since it coalesces multiple layout passes in the same tick.
|
|
253
|
-
*/ relayoutNow(context = this._invalidationContext || {}) {
|
|
254
|
-
// Cancel the scheduled relayout, since we're doing it now.
|
|
255
|
-
if (this._relayoutRaf) {
|
|
256
|
-
cancelAnimationFrame(this._relayoutRaf);
|
|
257
|
-
this._relayoutRaf = null;
|
|
258
|
-
// Update the provided context with the current invalidationContext since we are cancelling
|
|
259
|
-
// a scheduled relayoutNow call that has this._invalidationContext set as its default context arg (relayoutNow() in relayout)
|
|
260
|
-
context = {
|
|
261
|
-
...this._invalidationContext,
|
|
262
|
-
...context
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
// Reset the invalidation context
|
|
266
|
-
this._invalidationContext = null;
|
|
267
|
-
// Do nothing if we don't have a layout or content, or we are
|
|
268
|
-
// in the middle of an animated scroll transition.
|
|
269
|
-
if (!this.layout || !this._collection || this._scrollAnimation) return;
|
|
270
|
-
let scrollAnchor = this._getScrollAnchor();
|
|
271
|
-
// Trigger the beforeLayout hook, if provided
|
|
272
|
-
if (typeof context.beforeLayout === 'function') context.beforeLayout();
|
|
66
|
+
relayout(context = {}) {
|
|
273
67
|
// Validate the layout
|
|
274
68
|
this.layout.validate(context);
|
|
275
|
-
this.
|
|
276
|
-
//
|
|
277
|
-
if (typeof context.afterLayout === 'function') context.afterLayout();
|
|
278
|
-
// Adjust scroll position based on scroll anchor, and constrain.
|
|
69
|
+
this.contentSize = this.layout.getContentSize();
|
|
70
|
+
// Constrain scroll position.
|
|
279
71
|
// If the content changed, scroll to the top.
|
|
280
|
-
let visibleRect = this.
|
|
281
|
-
let
|
|
282
|
-
let
|
|
283
|
-
let contentOffsetY = context.contentChanged ? 0 : restoredScrollAnchor.y;
|
|
72
|
+
let visibleRect = this.visibleRect;
|
|
73
|
+
let contentOffsetX = context.contentChanged ? 0 : visibleRect.x;
|
|
74
|
+
let contentOffsetY = context.contentChanged ? 0 : visibleRect.y;
|
|
284
75
|
contentOffsetX = Math.max(0, Math.min(this.contentSize.width - visibleRect.width, contentOffsetX));
|
|
285
76
|
contentOffsetY = Math.max(0, Math.min(this.contentSize.height - visibleRect.height, contentOffsetY));
|
|
286
|
-
let hasLayoutUpdates = false;
|
|
287
77
|
if (contentOffsetX !== visibleRect.x || contentOffsetY !== visibleRect.y) {
|
|
288
|
-
// If
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
// the scroll animation and the content animation.
|
|
293
|
-
if (context.animated || !this._animatedContentOffset.isOrigin()) {
|
|
294
|
-
this._animatedContentOffset.x += visibleRect.x - contentOffsetX;
|
|
295
|
-
this._animatedContentOffset.y += visibleRect.y - contentOffsetY;
|
|
296
|
-
hasLayoutUpdates = this.updateSubviews(context.contentChanged);
|
|
297
|
-
} else this._setContentOffset(new (0, $3041db3296945e6e$export$baf26146a414f24a)(contentOffsetX, contentOffsetY));
|
|
298
|
-
} else hasLayoutUpdates = this.updateSubviews(context.contentChanged);
|
|
299
|
-
// Apply layout infos, unless this is coming from an animated transaction
|
|
300
|
-
if (!(context.transaction && context.animated)) this._applyLayoutInfos();
|
|
301
|
-
// Wait for animations, and apply the afterAnimation hook, if provided
|
|
302
|
-
if (context.animated && hasLayoutUpdates) {
|
|
303
|
-
this._enableTransitions();
|
|
304
|
-
let done = ()=>{
|
|
305
|
-
this._disableTransitions();
|
|
306
|
-
// Reset scroll position after animations (see above comment).
|
|
307
|
-
if (!this._animatedContentOffset.isOrigin()) {
|
|
308
|
-
// Get the content offset to scroll to, taking _animatedContentOffset into account.
|
|
309
|
-
let { x: x, y: y } = this.getVisibleRect();
|
|
310
|
-
this._resetAnimatedContentOffset();
|
|
311
|
-
this._setContentOffset(new (0, $3041db3296945e6e$export$baf26146a414f24a)(x, y));
|
|
312
|
-
}
|
|
313
|
-
if (typeof context.afterAnimation === 'function') context.afterAnimation();
|
|
314
|
-
};
|
|
315
|
-
// Sometimes the animation takes slightly longer than expected.
|
|
316
|
-
setTimeout(done, this.transitionDuration + 100);
|
|
317
|
-
return;
|
|
318
|
-
} else if (typeof context.afterAnimation === 'function') context.afterAnimation();
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Corrects DOM order of visible views to match item order of collection.
|
|
322
|
-
*/ _correctItemOrder() {
|
|
323
|
-
// Defer until after scrolling and animated transactions are complete
|
|
324
|
-
if (this._isScrolling || this._transaction) return;
|
|
325
|
-
for (let key of this._visibleLayoutInfos.keys()){
|
|
326
|
-
let view = this._visibleViews.get(key);
|
|
327
|
-
this._children.delete(view);
|
|
328
|
-
this._children.add(view);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
_enableTransitions() {
|
|
332
|
-
this.delegate.beginAnimations();
|
|
333
|
-
}
|
|
334
|
-
_disableTransitions() {
|
|
335
|
-
this.delegate.endAnimations();
|
|
336
|
-
}
|
|
337
|
-
_getScrollAnchor() {
|
|
338
|
-
if (!this.anchorScrollPosition) return null;
|
|
339
|
-
let visibleRect = this.getVisibleRect();
|
|
340
|
-
// Ask the delegate to provide a scroll anchor, if possible
|
|
341
|
-
if (this.delegate.getScrollAnchor) {
|
|
342
|
-
let key = this.delegate.getScrollAnchor(visibleRect);
|
|
343
|
-
if (key != null) {
|
|
344
|
-
let layoutInfo = this.layout.getLayoutInfo(key);
|
|
345
|
-
let corner = layoutInfo.rect.getCornerInRect(visibleRect);
|
|
346
|
-
if (corner) {
|
|
347
|
-
let key = layoutInfo.key;
|
|
348
|
-
let offset = layoutInfo.rect[corner].y - visibleRect.y;
|
|
349
|
-
return {
|
|
350
|
-
key: key,
|
|
351
|
-
layoutInfo: layoutInfo,
|
|
352
|
-
corner: corner,
|
|
353
|
-
offset: offset
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
// No need to anchor the scroll position if it is at the top
|
|
359
|
-
if (visibleRect.y === 0 && !this.anchorScrollPositionAtTop) return null;
|
|
360
|
-
// Find a view with a visible corner that has the smallest distance to the top of the collection view
|
|
361
|
-
let cornerAnchor = null;
|
|
362
|
-
for (let [key, view] of this._visibleViews){
|
|
363
|
-
let layoutInfo = view.layoutInfo;
|
|
364
|
-
if (layoutInfo && layoutInfo.rect.area > 0) {
|
|
365
|
-
let corner = layoutInfo.rect.getCornerInRect(visibleRect);
|
|
366
|
-
if (corner) {
|
|
367
|
-
let offset = layoutInfo.rect[corner].y - visibleRect.y;
|
|
368
|
-
if (!cornerAnchor || offset < cornerAnchor.offset) cornerAnchor = {
|
|
369
|
-
key: key,
|
|
370
|
-
layoutInfo: layoutInfo,
|
|
371
|
-
corner: corner,
|
|
372
|
-
offset: offset
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return cornerAnchor;
|
|
378
|
-
}
|
|
379
|
-
_restoreScrollAnchor(scrollAnchor, context) {
|
|
380
|
-
let contentOffset = this.getVisibleRect();
|
|
381
|
-
if (scrollAnchor) {
|
|
382
|
-
var _context_transaction;
|
|
383
|
-
let finalAnchor = ((_context_transaction = context.transaction) === null || _context_transaction === void 0 ? void 0 : _context_transaction.animated) ? context.transaction.finalMap.get(scrollAnchor.key) : this.layout.getLayoutInfo(scrollAnchor.layoutInfo.key);
|
|
384
|
-
if (finalAnchor) {
|
|
385
|
-
let adjustment = finalAnchor.rect[scrollAnchor.corner].y - contentOffset.y - scrollAnchor.offset;
|
|
386
|
-
contentOffset.y += adjustment;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return contentOffset;
|
|
390
|
-
}
|
|
391
|
-
getVisibleRect() {
|
|
392
|
-
let v = this.visibleRect;
|
|
393
|
-
let x = v.x - this._animatedContentOffset.x;
|
|
394
|
-
let y = v.y - this._animatedContentOffset.y;
|
|
395
|
-
return new (0, $60423f92c7f9ad87$export$c79fc6492f3af13d)(x, y, v.width, v.height);
|
|
78
|
+
// If the offset changed, trigger a new re-render.
|
|
79
|
+
let rect = new (0, $60423f92c7f9ad87$export$c79fc6492f3af13d)(contentOffsetX, contentOffsetY, visibleRect.width, visibleRect.height);
|
|
80
|
+
this.delegate.setVisibleRect(rect);
|
|
81
|
+
} else this.updateSubviews();
|
|
396
82
|
}
|
|
397
83
|
getVisibleLayoutInfos() {
|
|
398
84
|
let isTestEnv = false;
|
|
399
|
-
let isClientWidthMocked = Object.getOwnPropertyNames(
|
|
400
|
-
let isClientHeightMocked = Object.getOwnPropertyNames(
|
|
85
|
+
let isClientWidthMocked = isTestEnv && typeof HTMLElement !== 'undefined' && Object.getOwnPropertyNames(HTMLElement.prototype).includes('clientWidth');
|
|
86
|
+
let isClientHeightMocked = isTestEnv && typeof HTMLElement !== 'undefined' && Object.getOwnPropertyNames(HTMLElement.prototype).includes('clientHeight');
|
|
401
87
|
let rect;
|
|
402
|
-
if (isTestEnv && !(isClientWidthMocked && isClientHeightMocked)) rect = this.
|
|
403
|
-
else rect = this.
|
|
404
|
-
this._visibleLayoutInfos = this._getLayoutInfoMap(rect);
|
|
405
|
-
return this._visibleLayoutInfos;
|
|
406
|
-
}
|
|
407
|
-
_getLayoutInfoMap(rect, copy = false) {
|
|
88
|
+
if (isTestEnv && !(isClientWidthMocked && isClientHeightMocked)) rect = new (0, $60423f92c7f9ad87$export$c79fc6492f3af13d)(0, 0, this.contentSize.width, this.contentSize.height);
|
|
89
|
+
else rect = this._overscanManager.getOverscannedRect();
|
|
408
90
|
let layoutInfos = this.layout.getVisibleLayoutInfos(rect);
|
|
409
91
|
let map = new Map;
|
|
410
|
-
for (let layoutInfo of layoutInfos)
|
|
411
|
-
if (copy) layoutInfo = layoutInfo.copy();
|
|
412
|
-
map.set(layoutInfo.key, layoutInfo);
|
|
413
|
-
}
|
|
92
|
+
for (let layoutInfo of layoutInfos)map.set(layoutInfo.key, layoutInfo);
|
|
414
93
|
return map;
|
|
415
94
|
}
|
|
416
|
-
updateSubviews(
|
|
417
|
-
if (!this._collection) return;
|
|
95
|
+
updateSubviews() {
|
|
418
96
|
let visibleLayoutInfos = this.getVisibleLayoutInfos();
|
|
419
|
-
let currentlyVisible = this._visibleViews;
|
|
420
|
-
let toAdd, toRemove, toUpdate;
|
|
421
|
-
// If this is a force update, remove and re-add all views.
|
|
422
|
-
// Otherwise, find and update the diff.
|
|
423
|
-
if (forceUpdate) {
|
|
424
|
-
toAdd = visibleLayoutInfos;
|
|
425
|
-
toRemove = currentlyVisible;
|
|
426
|
-
toUpdate = new Set();
|
|
427
|
-
} else {
|
|
428
|
-
({ toAdd: toAdd, toRemove: toRemove, toUpdate: toUpdate } = (0, $fc36f9a046a9ce79$export$acaf96a27438246b)(currentlyVisible, visibleLayoutInfos));
|
|
429
|
-
for (let key of toUpdate){
|
|
430
|
-
let view = currentlyVisible.get(key);
|
|
431
|
-
if (!view || !view.layoutInfo) continue;
|
|
432
|
-
let item = this.getItem(visibleLayoutInfos.get(key).key);
|
|
433
|
-
if (view.content === item) toUpdate.delete(key);
|
|
434
|
-
else {
|
|
435
|
-
// If the view type changes, delete and recreate the view instead of updating
|
|
436
|
-
let { reuseType: reuseType } = this._getReuseType(view.layoutInfo, item);
|
|
437
|
-
if (view.viewType !== reuseType) {
|
|
438
|
-
toUpdate.delete(key);
|
|
439
|
-
toAdd.add(key);
|
|
440
|
-
toRemove.add(key);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
// We are done if the sets are equal
|
|
445
|
-
if (toAdd.size === 0 && toRemove.size === 0 && toUpdate.size === 0) {
|
|
446
|
-
if (this._transaction) this._applyLayoutInfos();
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
// Track views that should be removed. They are not removed from
|
|
451
|
-
// the DOM immediately, since we may reuse and need to re-insert
|
|
452
|
-
// them back into the DOM anyway.
|
|
453
97
|
let removed = new Set();
|
|
454
|
-
for (let key of
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
this._visibleViews.delete(key);
|
|
459
|
-
// If we are in the middle of a transaction, wait until the end
|
|
460
|
-
// of the animations to remove the views from the DOM. Also means
|
|
461
|
-
// we can't reuse those views immediately.
|
|
462
|
-
if (this._transaction) this._transaction.toRemove.set(key, view);
|
|
463
|
-
else this.reuseView(view);
|
|
464
|
-
}
|
|
98
|
+
for (let [key, view] of this._visibleViews)if (!visibleLayoutInfos.has(key)) {
|
|
99
|
+
this._visibleViews.delete(key);
|
|
100
|
+
view.parent.reuseChild(view);
|
|
101
|
+
removed.add(view); // Defer removing in case we reuse this view.
|
|
465
102
|
}
|
|
466
|
-
for (let key of
|
|
467
|
-
let
|
|
468
|
-
let view;
|
|
469
|
-
// If we're in a transaction, and a layout change happens
|
|
470
|
-
// during the animations such that a view that was going
|
|
471
|
-
// to be removed is now not, we don't create a new view
|
|
472
|
-
// since the old one is still in the DOM, marked as toRemove.
|
|
473
|
-
if (this._transaction) {
|
|
474
|
-
// if transaction, get initial layout attributes for the animation
|
|
475
|
-
if (this._transaction.initialLayoutInfo.has(key)) layoutInfo = this._transaction.initialLayoutInfo.get(key);
|
|
476
|
-
view = this._transaction.toRemove.get(key);
|
|
477
|
-
if (view) {
|
|
478
|
-
this._transaction.toRemove.delete(key);
|
|
479
|
-
this._applyLayoutInfo(view, layoutInfo);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
103
|
+
for (let [key, layoutInfo] of visibleLayoutInfos){
|
|
104
|
+
let view = this._visibleViews.get(key);
|
|
482
105
|
if (!view) {
|
|
483
|
-
// Create or reuse a view for this row
|
|
484
106
|
view = this.getReusableView(layoutInfo);
|
|
485
|
-
|
|
486
|
-
|
|
107
|
+
view.parent.children.add(view);
|
|
108
|
+
this._visibleViews.set(key, view);
|
|
109
|
+
removed.delete(view);
|
|
110
|
+
} else {
|
|
111
|
+
view.layoutInfo = layoutInfo;
|
|
112
|
+
let item = this.collection.getItem(layoutInfo.key);
|
|
113
|
+
if (view.content !== item) {
|
|
114
|
+
this._renderedContent.delete(view.content);
|
|
115
|
+
this._renderView(view);
|
|
116
|
+
}
|
|
487
117
|
}
|
|
488
|
-
this._visibleViews.set(key, view);
|
|
489
|
-
removed.delete(view);
|
|
490
|
-
}
|
|
491
|
-
for (let key of toUpdate){
|
|
492
|
-
let view = currentlyVisible.get(key);
|
|
493
|
-
this._renderedContent.delete(key);
|
|
494
|
-
this._renderView(view);
|
|
495
118
|
}
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
119
|
+
// The remaining views in `removed` were not reused to render new items.
|
|
120
|
+
// They should be removed from the DOM. We also clear the reusable view queue
|
|
121
|
+
// here since there's no point holding onto views that have been removed.
|
|
122
|
+
// Doing so hurts performance in the future when reusing elements due to FIFO order.
|
|
123
|
+
for (let view of removed){
|
|
124
|
+
view.parent.children.delete(view);
|
|
125
|
+
view.parent.reusableViews.clear();
|
|
126
|
+
}
|
|
127
|
+
// Reordering DOM nodes is costly, so we defer this until scrolling stops.
|
|
128
|
+
// DOM order does not affect visual order (due to absolute positioning),
|
|
129
|
+
// but does matter for assistive technology users.
|
|
130
|
+
if (!this._isScrolling) // Layout infos must be in topological order (parents before children).
|
|
131
|
+
for (let key of visibleLayoutInfos.keys()){
|
|
132
|
+
let view = this._visibleViews.get(key);
|
|
133
|
+
view.parent.children.delete(view);
|
|
134
|
+
view.parent.children.add(view);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Performs layout and updates visible views as needed. */ render(opts) {
|
|
138
|
+
let mutableThis = this;
|
|
139
|
+
let needsLayout = false;
|
|
140
|
+
let offsetChanged = false;
|
|
141
|
+
let sizeChanged = false;
|
|
142
|
+
let itemSizeChanged = false;
|
|
143
|
+
let needsUpdate = false;
|
|
144
|
+
if (opts.collection !== this.collection) {
|
|
145
|
+
mutableThis.collection = opts.collection;
|
|
146
|
+
needsLayout = true;
|
|
147
|
+
}
|
|
148
|
+
if (opts.layout !== this.layout) {
|
|
149
|
+
if (this.layout) this.layout.virtualizer = null;
|
|
150
|
+
opts.layout.virtualizer = this;
|
|
151
|
+
mutableThis.layout = opts.layout;
|
|
152
|
+
needsLayout = true;
|
|
153
|
+
}
|
|
154
|
+
if (opts.persistedKeys && !(0, $fc36f9a046a9ce79$export$a8d0d0c8d1c5df64)(opts.persistedKeys, this.persistedKeys)) {
|
|
155
|
+
mutableThis.persistedKeys = opts.persistedKeys;
|
|
156
|
+
needsUpdate = true;
|
|
157
|
+
}
|
|
158
|
+
if (!this.visibleRect.equals(opts.visibleRect)) {
|
|
159
|
+
this._overscanManager.setVisibleRect(opts.visibleRect);
|
|
160
|
+
let shouldInvalidate = this.layout.shouldInvalidate(opts.visibleRect, this.visibleRect);
|
|
161
|
+
if (shouldInvalidate) {
|
|
162
|
+
offsetChanged = !opts.visibleRect.pointEquals(this.visibleRect);
|
|
163
|
+
sizeChanged = !opts.visibleRect.sizeEquals(this.visibleRect);
|
|
164
|
+
needsLayout = true;
|
|
165
|
+
} else needsUpdate = true;
|
|
166
|
+
mutableThis.visibleRect = opts.visibleRect;
|
|
167
|
+
}
|
|
168
|
+
if (opts.invalidationContext !== this._invalidationContext) {
|
|
169
|
+
if (opts.invalidationContext) {
|
|
170
|
+
sizeChanged || (sizeChanged = opts.invalidationContext.sizeChanged || false);
|
|
171
|
+
offsetChanged || (offsetChanged = opts.invalidationContext.offsetChanged || false);
|
|
172
|
+
itemSizeChanged || (itemSizeChanged = opts.invalidationContext.itemSizeChanged || false);
|
|
173
|
+
needsLayout || (needsLayout = itemSizeChanged || sizeChanged || offsetChanged);
|
|
174
|
+
needsLayout || (needsLayout = opts.invalidationContext.layoutOptions !== this._invalidationContext.layoutOptions);
|
|
175
|
+
}
|
|
176
|
+
this._invalidationContext = opts.invalidationContext;
|
|
177
|
+
}
|
|
178
|
+
if (opts.isScrolling !== this._isScrolling) {
|
|
179
|
+
this._isScrolling = opts.isScrolling;
|
|
180
|
+
if (!opts.isScrolling) // Update to fix the DOM order after scrolling.
|
|
181
|
+
needsUpdate = true;
|
|
182
|
+
}
|
|
183
|
+
if (needsLayout) this.relayout({
|
|
184
|
+
offsetChanged: offsetChanged,
|
|
185
|
+
sizeChanged: sizeChanged,
|
|
186
|
+
itemSizeChanged: itemSizeChanged,
|
|
187
|
+
layoutOptions: this._invalidationContext.layoutOptions
|
|
505
188
|
});
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
afterRender() {
|
|
509
|
-
if (this._transactionQueue.length > 0) this._processTransactionQueue();
|
|
510
|
-
else if (this._invalidationContext) this.relayoutNow();
|
|
511
|
-
if (this.shouldOverscan) this._overscanManager.collectMetrics();
|
|
512
|
-
}
|
|
513
|
-
_flushVisibleViews() {
|
|
514
|
-
// CollectionVirtualizer deals with a flattened set of LayoutInfos, but they can represent hierarchy
|
|
515
|
-
// by referencing a parentKey. Just before rendering the visible views, we rebuild this hierarchy
|
|
516
|
-
// by creating a mapping of views by parent key and recursively calling the delegate's renderWrapper
|
|
517
|
-
// method to build the final tree.
|
|
518
|
-
this._viewsByParentKey = new Map([
|
|
519
|
-
[
|
|
520
|
-
null,
|
|
521
|
-
[]
|
|
522
|
-
]
|
|
523
|
-
]);
|
|
524
|
-
for (let view of this._children){
|
|
525
|
-
var _view_layoutInfo, _this__viewsByParentKey_get, _view_layoutInfo1, _view_layoutInfo2, _view_layoutInfo3;
|
|
526
|
-
if (((_view_layoutInfo = view.layoutInfo) === null || _view_layoutInfo === void 0 ? void 0 : _view_layoutInfo.parentKey) != null && !this._viewsByParentKey.has(view.layoutInfo.parentKey)) this._viewsByParentKey.set(view.layoutInfo.parentKey, []);
|
|
527
|
-
(_this__viewsByParentKey_get = this._viewsByParentKey.get((_view_layoutInfo1 = view.layoutInfo) === null || _view_layoutInfo1 === void 0 ? void 0 : _view_layoutInfo1.parentKey)) === null || _this__viewsByParentKey_get === void 0 ? void 0 : _this__viewsByParentKey_get.push(view);
|
|
528
|
-
if (!this._viewsByParentKey.has((_view_layoutInfo2 = view.layoutInfo) === null || _view_layoutInfo2 === void 0 ? void 0 : _view_layoutInfo2.key)) this._viewsByParentKey.set((_view_layoutInfo3 = view.layoutInfo) === null || _view_layoutInfo3 === void 0 ? void 0 : _view_layoutInfo3.key, []);
|
|
529
|
-
}
|
|
530
|
-
let children = this.getChildren(null);
|
|
531
|
-
this.delegate.setVisibleViews(children);
|
|
189
|
+
else if (needsUpdate) this.updateSubviews();
|
|
190
|
+
return this.getChildren(null);
|
|
532
191
|
}
|
|
533
192
|
getChildren(key) {
|
|
534
|
-
let
|
|
535
|
-
|
|
536
|
-
return this.delegate.renderWrapper(parent, view, children, (childViews)=>
|
|
193
|
+
let parent = key == null ? this._rootView : this._visibleViews.get(key);
|
|
194
|
+
let renderChildren = (parent, views)=>views.map((view)=>{
|
|
195
|
+
return this.delegate.renderWrapper(parent, view, view.children ? Array.from(view.children) : [], (childViews)=>renderChildren(view, childViews));
|
|
537
196
|
});
|
|
538
|
-
|
|
539
|
-
return buildTree(parent, this._viewsByParentKey.get(key));
|
|
540
|
-
}
|
|
541
|
-
_applyLayoutInfo(view, layoutInfo) {
|
|
542
|
-
if (view.layoutInfo === layoutInfo) return false;
|
|
543
|
-
view.layoutInfo = layoutInfo;
|
|
544
|
-
return true;
|
|
545
|
-
}
|
|
546
|
-
_applyLayoutInfos() {
|
|
547
|
-
let updated = false;
|
|
548
|
-
// Apply layout infos to visible views
|
|
549
|
-
for (let view of this._visibleViews.values()){
|
|
550
|
-
let cur = view.layoutInfo;
|
|
551
|
-
if ((cur === null || cur === void 0 ? void 0 : cur.key) != null) {
|
|
552
|
-
let layoutInfo = this.layout.getLayoutInfo(cur.key);
|
|
553
|
-
if (this._applyLayoutInfo(view, layoutInfo)) updated = true;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
// Apply final layout infos for views that will be removed
|
|
557
|
-
if (this._transaction) {
|
|
558
|
-
for (let view of this._transaction.toRemove.values()){
|
|
559
|
-
let cur = view.layoutInfo;
|
|
560
|
-
if ((cur === null || cur === void 0 ? void 0 : cur.key) != null) {
|
|
561
|
-
let layoutInfo = this.layout.getLayoutInfo(cur.key);
|
|
562
|
-
if (this._applyLayoutInfo(view, layoutInfo)) updated = true;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
for (let view of this._transaction.removed.values()){
|
|
566
|
-
let cur = view.layoutInfo;
|
|
567
|
-
let layoutInfo = this._transaction.finalLayoutInfo.get(cur.key) || cur;
|
|
568
|
-
layoutInfo = this.layout.getFinalLayoutInfo(layoutInfo.copy());
|
|
569
|
-
if (this._applyLayoutInfo(view, layoutInfo)) updated = true;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
if (updated) this._flushVisibleViews();
|
|
573
|
-
}
|
|
574
|
-
_hasLayoutUpdates() {
|
|
575
|
-
if (!this._transaction) return false;
|
|
576
|
-
for (let view of this._visibleViews.values()){
|
|
577
|
-
let cur = view.layoutInfo;
|
|
578
|
-
if (!cur) return true;
|
|
579
|
-
let layoutInfo = this.layout.getLayoutInfo(cur.key);
|
|
580
|
-
if (// Uses equals rather than pointEquals so that width/height changes are taken into account
|
|
581
|
-
!cur.rect.equals(layoutInfo.rect) || cur.opacity !== layoutInfo.opacity || cur.transform !== layoutInfo.transform) return true;
|
|
582
|
-
}
|
|
583
|
-
return false;
|
|
197
|
+
return renderChildren(parent, Array.from(parent.children));
|
|
584
198
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
this._reusableViews[view.viewType].push(view);
|
|
588
|
-
}
|
|
589
|
-
removeViews(toRemove) {
|
|
590
|
-
for (let view of toRemove)this._children.delete(view);
|
|
199
|
+
invalidate(context) {
|
|
200
|
+
this.delegate.invalidate(context);
|
|
591
201
|
}
|
|
592
202
|
updateItemSize(key, size) {
|
|
593
|
-
// TODO: we should be able to invalidate a single index path
|
|
594
|
-
// @ts-ignore
|
|
595
203
|
if (!this.layout.updateItemSize) return;
|
|
596
|
-
// If the scroll position is currently animating, add the update
|
|
597
|
-
// to a queue to be processed after the animation is complete.
|
|
598
|
-
if (this._scrollAnimation) {
|
|
599
|
-
this._sizeUpdateQueue.set(key, size);
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
// @ts-ignore
|
|
603
204
|
let changed = this.layout.updateItemSize(key, size);
|
|
604
|
-
if (changed) this.
|
|
605
|
-
|
|
606
|
-
startScrolling() {
|
|
607
|
-
this._isScrolling = true;
|
|
608
|
-
}
|
|
609
|
-
endScrolling() {
|
|
610
|
-
this._isScrolling = false;
|
|
611
|
-
this._correctItemOrder();
|
|
612
|
-
this._flushVisibleViews();
|
|
613
|
-
}
|
|
614
|
-
_resetAnimatedContentOffset() {
|
|
615
|
-
// Reset the animated content offset of subviews. See comment in relayoutNow for details.
|
|
616
|
-
if (!this._animatedContentOffset.isOrigin()) {
|
|
617
|
-
this._animatedContentOffset = new (0, $3041db3296945e6e$export$baf26146a414f24a)(0, 0);
|
|
618
|
-
this._applyLayoutInfos();
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Scrolls the item with the given key into view, optionally with an animation.
|
|
623
|
-
* @param key The key of the item to scroll into view.
|
|
624
|
-
* @param duration The duration of the scroll animation.
|
|
625
|
-
*/ scrollToItem(key, options) {
|
|
626
|
-
// key can be 0, so check if null or undefined
|
|
627
|
-
if (key == null) return;
|
|
628
|
-
let layoutInfo = this.layout.getLayoutInfo(key);
|
|
629
|
-
if (!layoutInfo) return;
|
|
630
|
-
let { duration: duration = 300, shouldScrollX: shouldScrollX = true, shouldScrollY: shouldScrollY = true, offsetX: offsetX = 0, offsetY: offsetY = 0 } = options;
|
|
631
|
-
let x = this.visibleRect.x;
|
|
632
|
-
let y = this.visibleRect.y;
|
|
633
|
-
let minX = layoutInfo.rect.x - offsetX;
|
|
634
|
-
let minY = layoutInfo.rect.y - offsetY;
|
|
635
|
-
let maxX = x + this.visibleRect.width;
|
|
636
|
-
let maxY = y + this.visibleRect.height;
|
|
637
|
-
if (shouldScrollX) {
|
|
638
|
-
if (minX <= x || maxX === 0) x = minX;
|
|
639
|
-
else if (layoutInfo.rect.maxX > maxX) x += layoutInfo.rect.maxX - maxX;
|
|
640
|
-
}
|
|
641
|
-
if (shouldScrollY) {
|
|
642
|
-
if (minY <= y || maxY === 0) y = minY;
|
|
643
|
-
else if (layoutInfo.rect.maxY > maxY) y += layoutInfo.rect.maxY - maxY;
|
|
644
|
-
}
|
|
645
|
-
return this.scrollTo(new (0, $3041db3296945e6e$export$baf26146a414f24a)(x, y), duration);
|
|
646
|
-
}
|
|
647
|
-
/**
|
|
648
|
-
* Performs an animated scroll to the given offset.
|
|
649
|
-
* @param offset - The offset to scroll to.
|
|
650
|
-
* @param duration The duration of the animation.
|
|
651
|
-
* @returns A promise that resolves when the animation is complete.
|
|
652
|
-
*/ scrollTo(offset, duration = 300) {
|
|
653
|
-
// Cancel the current scroll animation
|
|
654
|
-
if (this._scrollAnimation) {
|
|
655
|
-
this._scrollAnimation.cancel();
|
|
656
|
-
this._scrollAnimation = null;
|
|
657
|
-
}
|
|
658
|
-
// Set the content offset synchronously if the duration is zero
|
|
659
|
-
if (duration <= 0 || this.visibleRect.pointEquals(offset)) {
|
|
660
|
-
this._setContentOffset(offset);
|
|
661
|
-
return Promise.resolve();
|
|
662
|
-
}
|
|
663
|
-
this.startScrolling();
|
|
664
|
-
this._scrollAnimation = (0, $3eb131dcf37ad5f8$export$dc0b63720788090c)(this.visibleRect, offset, duration, (0, $3eb131dcf37ad5f8$export$57636bb43b1ccbb0), (offset)=>{
|
|
665
|
-
this._setContentOffset(offset);
|
|
205
|
+
if (changed) this.invalidate({
|
|
206
|
+
itemSizeChanged: true
|
|
666
207
|
});
|
|
667
|
-
this._scrollAnimation.then(()=>{
|
|
668
|
-
this._scrollAnimation = null;
|
|
669
|
-
// Process view size updates that occurred during the animation.
|
|
670
|
-
// Only views that are still visible will be actually updated.
|
|
671
|
-
for (let [key, size] of this._sizeUpdateQueue)this.updateItemSize(key, size);
|
|
672
|
-
this._sizeUpdateQueue.clear();
|
|
673
|
-
this.relayout();
|
|
674
|
-
this._processTransactionQueue();
|
|
675
|
-
this.endScrolling();
|
|
676
|
-
});
|
|
677
|
-
return this._scrollAnimation;
|
|
678
|
-
}
|
|
679
|
-
_runTransaction(action, animated) {
|
|
680
|
-
this._startTransaction();
|
|
681
|
-
if (this._nextTransaction) this._nextTransaction.actions.push(action);
|
|
682
|
-
this._endTransaction(animated);
|
|
683
208
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
this.
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (!this._nextTransaction) return false;
|
|
690
|
-
// Save whether the transaction should be animated.
|
|
691
|
-
if (animated != null) this._nextTransaction.animated = animated;
|
|
692
|
-
// If we haven't reached level 0, we are still in a
|
|
693
|
-
// nested transaction. Wait for the parent to end.
|
|
694
|
-
if (--this._nextTransaction.level > 0) return false;
|
|
695
|
-
// Do nothing for empty transactions
|
|
696
|
-
if (this._nextTransaction.actions.length === 0) {
|
|
697
|
-
this._nextTransaction = null;
|
|
698
|
-
return false;
|
|
699
|
-
}
|
|
700
|
-
// Default animations to true
|
|
701
|
-
if (this._nextTransaction.animated == null) this._nextTransaction.animated = true;
|
|
702
|
-
// Enqueue the transaction
|
|
703
|
-
this._transactionQueue.push(this._nextTransaction);
|
|
704
|
-
this._nextTransaction = null;
|
|
705
|
-
return true;
|
|
706
|
-
}
|
|
707
|
-
_processTransactionQueue() {
|
|
708
|
-
// If the current transaction is animating, wait until the end
|
|
709
|
-
// to process the next transaction.
|
|
710
|
-
if (this._transaction || this._scrollAnimation) return;
|
|
711
|
-
let next = this._transactionQueue.shift();
|
|
712
|
-
if (next) this._performTransaction(next);
|
|
713
|
-
}
|
|
714
|
-
_getContentRect() {
|
|
715
|
-
return new (0, $60423f92c7f9ad87$export$c79fc6492f3af13d)(0, 0, this.contentSize.width, this.contentSize.height);
|
|
716
|
-
}
|
|
717
|
-
_performTransaction(transaction) {
|
|
718
|
-
this._transaction = transaction;
|
|
719
|
-
this.relayoutNow({
|
|
720
|
-
transaction: transaction,
|
|
721
|
-
animated: transaction.animated,
|
|
722
|
-
beforeLayout: ()=>{
|
|
723
|
-
// Get the initial layout infos for all views before the updates
|
|
724
|
-
// so we can figure out which views to add and remove.
|
|
725
|
-
if (transaction.animated) transaction.initialMap = this._getLayoutInfoMap(this._getContentRect(), true);
|
|
726
|
-
// Apply the actions that occurred during this transaction
|
|
727
|
-
for (let action of transaction.actions)action();
|
|
728
|
-
},
|
|
729
|
-
afterLayout: ()=>{
|
|
730
|
-
// Get the final layout infos after the updates
|
|
731
|
-
if (transaction.animated) {
|
|
732
|
-
transaction.finalMap = this._getLayoutInfoMap(this._getContentRect());
|
|
733
|
-
this._setupTransactionAnimations(transaction);
|
|
734
|
-
} else this._transaction = null;
|
|
735
|
-
},
|
|
736
|
-
afterAnimation: ()=>{
|
|
737
|
-
// Remove and reuse views when animations are done
|
|
738
|
-
if (transaction.toRemove.size > 0 || transaction.removed.size > 0) for (let view of (0, $fc36f9a046a9ce79$export$cfc14088dfefce5f)(transaction.toRemove.values(), transaction.removed.values())){
|
|
739
|
-
this._children.delete(view);
|
|
740
|
-
this.reuseView(view);
|
|
741
|
-
}
|
|
742
|
-
this._transaction = null;
|
|
743
|
-
// Ensure DOM order is correct for accessibility after animations are complete
|
|
744
|
-
this._correctItemOrder();
|
|
745
|
-
this._flushVisibleViews();
|
|
746
|
-
this._processTransactionQueue();
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
_setupTransactionAnimations(transaction) {
|
|
751
|
-
let { initialMap: initialMap, finalMap: finalMap } = transaction;
|
|
752
|
-
// Store initial and final layout infos for animations
|
|
753
|
-
for (let [key, layoutInfo] of initialMap)if (finalMap.has(key)) // Store the initial layout info for use during animations.
|
|
754
|
-
transaction.initialLayoutInfo.set(key, layoutInfo);
|
|
755
|
-
else // This view was removed. Store the layout info for use
|
|
756
|
-
// in Layout#getFinalLayoutInfo during animations.
|
|
757
|
-
transaction.finalLayoutInfo.set(layoutInfo.key, layoutInfo);
|
|
758
|
-
// Get initial layout infos for views that were added
|
|
759
|
-
for (let [key, layoutInfo] of finalMap)if (!initialMap.has(key)) {
|
|
760
|
-
let initialLayoutInfo = this.layout.getInitialLayoutInfo(layoutInfo.copy());
|
|
761
|
-
transaction.initialLayoutInfo.set(key, initialLayoutInfo);
|
|
762
|
-
}
|
|
763
|
-
// Figure out which views were removed.
|
|
764
|
-
for (let [key, view] of this._visibleViews)// If an item has a width of 0, there is no need to remove it from the _visibleViews.
|
|
765
|
-
// Removing an item with width of 0 can cause a loop where the item gets added, removed,
|
|
766
|
-
// added, removed... etc in a loop.
|
|
767
|
-
if (!finalMap.has(key) && view.layoutInfo.rect.width > 0) {
|
|
768
|
-
transaction.removed.set(key, view);
|
|
769
|
-
this._visibleViews.delete(key);
|
|
770
|
-
// In case something weird happened, where we have a view but no
|
|
771
|
-
// initial layout info, use the one attached to the view.
|
|
772
|
-
if (view.layoutInfo) {
|
|
773
|
-
if (!transaction.finalLayoutInfo.has(view.layoutInfo.key)) transaction.finalLayoutInfo.set(view.layoutInfo.key, view.layoutInfo);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
constructor(options = {}){
|
|
778
|
-
this._contentSize = new (0, $ee1bfa90a957fb8a$export$cb6da89c6af1a8ec);
|
|
779
|
-
this._visibleRect = new (0, $60423f92c7f9ad87$export$c79fc6492f3af13d);
|
|
780
|
-
this._reusableViews = {};
|
|
781
|
-
this._visibleLayoutInfos = new Map();
|
|
209
|
+
constructor(delegate){
|
|
210
|
+
this.delegate = delegate;
|
|
211
|
+
this.contentSize = new (0, $ee1bfa90a957fb8a$export$cb6da89c6af1a8ec);
|
|
212
|
+
this.visibleRect = new (0, $60423f92c7f9ad87$export$c79fc6492f3af13d);
|
|
213
|
+
this.persistedKeys = new Set();
|
|
782
214
|
this._visibleViews = new Map();
|
|
783
215
|
this._renderedContent = new WeakMap();
|
|
784
|
-
this.
|
|
216
|
+
this._rootView = new (0, $ad1d98aa8f0c31b4$export$1a5223887c560441)(this);
|
|
217
|
+
this._isScrolling = false;
|
|
785
218
|
this._invalidationContext = null;
|
|
786
219
|
this._overscanManager = new (0, $364191b3decf3697$export$4455ee6afb38dcbb)();
|
|
787
|
-
this._persistedKeys = new Set();
|
|
788
|
-
this._scrollAnimation = null;
|
|
789
|
-
this._isScrolling = false;
|
|
790
|
-
this._sizeUpdateQueue = new Map();
|
|
791
|
-
this._animatedContentOffset = new (0, $3041db3296945e6e$export$baf26146a414f24a)(0, 0);
|
|
792
|
-
this._transaction = null;
|
|
793
|
-
this._nextTransaction = null;
|
|
794
|
-
this._transactionQueue = [];
|
|
795
|
-
var _options_transitionDuration;
|
|
796
|
-
// Set options from passed object if given
|
|
797
|
-
this.transitionDuration = (_options_transitionDuration = options.transitionDuration) !== null && _options_transitionDuration !== void 0 ? _options_transitionDuration : 500;
|
|
798
|
-
this.anchorScrollPosition = options.anchorScrollPosition || false;
|
|
799
|
-
this.anchorScrollPositionAtTop = options.anchorScrollPositionAtTop || false;
|
|
800
|
-
this.shouldOverscan = options.shouldOverscan !== false;
|
|
801
|
-
for (let key of [
|
|
802
|
-
'delegate',
|
|
803
|
-
'size',
|
|
804
|
-
'layout',
|
|
805
|
-
'collection'
|
|
806
|
-
])if (options[key]) this[key] = options[key];
|
|
807
220
|
}
|
|
808
221
|
}
|
|
809
222
|
|