@html-next/vertical-collection 4.0.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +64 -79
- package/{addon → src}/-private/data-view/elements/occluded-content.js +1 -1
- package/{addon/-private/data-view → src/-private/data-view/elements}/viewport-container.js +35 -25
- package/{addon → src}/-private/data-view/elements/virtual-component.js +13 -5
- package/{addon → src}/-private/data-view/radar/dynamic-radar.js +41 -40
- package/{addon → src}/-private/data-view/radar/radar.js +211 -112
- package/{addon → src}/-private/data-view/radar/static-radar.js +19 -7
- package/{addon → src}/-private/data-view/skip-list.js +20 -21
- package/{addon → src}/-private/data-view/utils/insert-range-before.js +6 -1
- package/{addon → src}/-private/data-view/utils/mutation-checkers.js +12 -4
- package/src/-private/data-view/utils/object-at.js +10 -0
- package/{addon → src}/-private/data-view/utils/scroll-handler.js +19 -9
- package/{addon → src}/-private/data-view/utils/supports-passive.js +2 -2
- package/{addon/-private/data-view/elements → src/-private/data-view}/viewport-container.js +35 -25
- package/{addon → src}/-private/ember-internals/key-for-item.js +9 -3
- package/{addon → src}/-private/index.js +8 -8
- package/{addon → src}/-private/utils/element/closest.js +8 -2
- package/{addon → src}/-private/utils/element/estimate-element-height.js +11 -5
- package/{addon/components/vertical-collection/component.js → src/components/vertical-collection.gjs} +155 -71
- package/src/index.js +3 -0
- package/{addon/styles/app.css → src/occluded-content.css} +11 -1
- package/.github/workflows/ci.yml +0 -102
- package/CHANGELOG.md +0 -176
- package/README.md +0 -122
- package/RELEASE.md +0 -74
- package/addon/-private/data-view/utils/object-at.js +0 -7
- package/addon/components/vertical-collection/template.hbs +0 -13
- package/app/components/vertical-collection.js +0 -1
- package/bin/restore-env.sh +0 -1
- package/bin/run-tests-with-retry.sh +0 -24
- package/bin/stash-env.sh +0 -1
- package/config/ember-cli-toolbelts.json +0 -1
- package/config/environment.js +0 -5
- package/index.js +0 -157
- package/vendor/debug.css +0 -62
- /package/{addon → src}/-private/data-view/utils/round-to.js +0 -0
- /package/{addon → src}/-private/ember-internals/identity.js +0 -0
- /package/{addon → src}/-private/utils/document-shim.js +0 -0
- /package/{addon → src}/-private/utils/element/get-scaled-client-rect.js +0 -0
|
@@ -6,26 +6,26 @@ import { DEBUG } from '@glimmer/env';
|
|
|
6
6
|
|
|
7
7
|
import { Token, scheduler } from 'ember-raf-scheduler';
|
|
8
8
|
|
|
9
|
-
import VirtualComponent from '../elements/virtual-component';
|
|
10
|
-
import OccludedContent from '../elements/occluded-content';
|
|
11
|
-
import insertRangeBefore from '../utils/insert-range-before';
|
|
12
|
-
import objectAt from '../utils/object-at';
|
|
13
|
-
import roundTo from '../utils/round-to';
|
|
14
|
-
import { isPrepend, isAppend } from '../utils/mutation-checkers';
|
|
9
|
+
import VirtualComponent from '../elements/virtual-component.js';
|
|
10
|
+
import OccludedContent from '../elements/occluded-content.js';
|
|
11
|
+
import insertRangeBefore from '../utils/insert-range-before.js';
|
|
12
|
+
import objectAt from '../utils/object-at.js';
|
|
13
|
+
import roundTo from '../utils/round-to.js';
|
|
14
|
+
import { isPrepend, isAppend } from '../utils/mutation-checkers.js';
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
17
|
addScrollHandler,
|
|
18
|
-
removeScrollHandler
|
|
19
|
-
} from '../utils/scroll-handler';
|
|
18
|
+
removeScrollHandler,
|
|
19
|
+
} from '../utils/scroll-handler.js';
|
|
20
20
|
|
|
21
|
-
import ViewportContainer from '../viewport-container';
|
|
21
|
+
import ViewportContainer from '../viewport-container.js';
|
|
22
22
|
|
|
23
|
-
import closestElement from '../../utils/element/closest';
|
|
24
|
-
import estimateElementHeight from '../../utils/element/estimate-element-height';
|
|
25
|
-
import getScaledClientRect from '../../utils/element/get-scaled-client-rect';
|
|
26
|
-
import keyForItem from '../../ember-internals/key-for-item';
|
|
23
|
+
import closestElement from '../../utils/element/closest.js';
|
|
24
|
+
import estimateElementHeight from '../../utils/element/estimate-element-height.js';
|
|
25
|
+
import getScaledClientRect from '../../utils/element/get-scaled-client-rect.js';
|
|
26
|
+
import keyForItem from '../../ember-internals/key-for-item.js';
|
|
27
27
|
|
|
28
|
-
import document from '../../utils/document-shim';
|
|
28
|
+
import document from '../../utils/document-shim.js';
|
|
29
29
|
|
|
30
30
|
export default class Radar {
|
|
31
31
|
constructor(
|
|
@@ -41,8 +41,8 @@ export default class Radar {
|
|
|
41
41
|
renderFromLast,
|
|
42
42
|
shouldRecycle,
|
|
43
43
|
startingIndex,
|
|
44
|
-
occlusionTagName
|
|
45
|
-
}
|
|
44
|
+
occlusionTagName,
|
|
45
|
+
},
|
|
46
46
|
) {
|
|
47
47
|
this.token = new Token(parentToken);
|
|
48
48
|
|
|
@@ -123,14 +123,17 @@ export default class Radar {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// Initialize virtual components
|
|
126
|
-
this.virtualComponents = A([
|
|
126
|
+
this.virtualComponents = A([
|
|
127
|
+
this._occludedContentBefore,
|
|
128
|
+
this._occludedContentAfter,
|
|
129
|
+
]);
|
|
127
130
|
this.orderedComponents = [];
|
|
128
131
|
|
|
129
132
|
this._updateVirtualComponents();
|
|
130
133
|
|
|
131
134
|
// In older versions of Ember/IE, binding anything on an object in the template
|
|
132
135
|
// adds observers which creates __ember_meta__
|
|
133
|
-
this.__ember_meta__ = null;
|
|
136
|
+
this.__ember_meta__ = null;
|
|
134
137
|
|
|
135
138
|
if (DEBUG) {
|
|
136
139
|
this._debugDidUpdate = null;
|
|
@@ -145,8 +148,14 @@ export default class Radar {
|
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
// Boundaries
|
|
148
|
-
this._occludedContentBefore.removeEventListener(
|
|
149
|
-
|
|
151
|
+
this._occludedContentBefore.removeEventListener(
|
|
152
|
+
'click',
|
|
153
|
+
this._pageUpHandler,
|
|
154
|
+
);
|
|
155
|
+
this._occludedContentAfter.removeEventListener(
|
|
156
|
+
'click',
|
|
157
|
+
this._pageDownHandler,
|
|
158
|
+
);
|
|
150
159
|
this._occludedContentBefore.destroy();
|
|
151
160
|
this._occludedContentAfter.destroy();
|
|
152
161
|
|
|
@@ -168,16 +177,15 @@ export default class Radar {
|
|
|
168
177
|
* sets up initial scroll state, and
|
|
169
178
|
*/
|
|
170
179
|
start() {
|
|
171
|
-
const {
|
|
172
|
-
startingIndex,
|
|
173
|
-
containerSelector,
|
|
174
|
-
_occludedContentBefore
|
|
175
|
-
} = this;
|
|
180
|
+
const { startingIndex, containerSelector, _occludedContentBefore } = this;
|
|
176
181
|
|
|
177
182
|
// Use the occluded content element, which has been inserted into the DOM,
|
|
178
183
|
// to find the item container and the scroll container
|
|
179
184
|
this._itemContainer = _occludedContentBefore.element.parentNode;
|
|
180
|
-
this._scrollContainer =
|
|
185
|
+
this._scrollContainer =
|
|
186
|
+
containerSelector === 'body'
|
|
187
|
+
? ViewportContainer
|
|
188
|
+
: closestElement(this._itemContainer, containerSelector);
|
|
181
189
|
|
|
182
190
|
this._updateConstants();
|
|
183
191
|
|
|
@@ -187,13 +195,14 @@ export default class Radar {
|
|
|
187
195
|
renderFromLast,
|
|
188
196
|
_calculatedEstimateHeight,
|
|
189
197
|
_collectionOffset,
|
|
190
|
-
_calculatedScrollContainerHeight
|
|
198
|
+
_calculatedScrollContainerHeight,
|
|
191
199
|
} = this;
|
|
192
200
|
|
|
193
201
|
let startingScrollTop = startingIndex * _calculatedEstimateHeight;
|
|
194
202
|
|
|
195
203
|
if (renderFromLast) {
|
|
196
|
-
startingScrollTop -=
|
|
204
|
+
startingScrollTop -=
|
|
205
|
+
_calculatedScrollContainerHeight - _calculatedEstimateHeight;
|
|
197
206
|
}
|
|
198
207
|
|
|
199
208
|
// initialize the scrollTop value, which will be applied to the
|
|
@@ -309,7 +318,9 @@ export default class Radar {
|
|
|
309
318
|
* pre-render and actual item size post-render.
|
|
310
319
|
*/
|
|
311
320
|
_calculateScrollDiff() {
|
|
312
|
-
return (
|
|
321
|
+
return (
|
|
322
|
+
this._prependOffset + this._scrollTop - this._scrollContainer.scrollTop
|
|
323
|
+
);
|
|
313
324
|
}
|
|
314
325
|
|
|
315
326
|
_determineUpdateType() {
|
|
@@ -320,14 +331,16 @@ export default class Radar {
|
|
|
320
331
|
|
|
321
332
|
_prevTotalItems,
|
|
322
333
|
_prevFirstKey,
|
|
323
|
-
_prevLastKey
|
|
334
|
+
_prevLastKey,
|
|
324
335
|
} = this;
|
|
325
336
|
|
|
326
337
|
const lenDiff = totalItems - _prevTotalItems;
|
|
327
338
|
|
|
328
339
|
if (isPrepend(lenDiff, items, key, _prevFirstKey, _prevLastKey) === true) {
|
|
329
340
|
this.prepend(lenDiff);
|
|
330
|
-
} else if (
|
|
341
|
+
} else if (
|
|
342
|
+
isAppend(lenDiff, items, key, _prevFirstKey, _prevLastKey) === true
|
|
343
|
+
) {
|
|
331
344
|
this.append(lenDiff);
|
|
332
345
|
} else {
|
|
333
346
|
this.reset();
|
|
@@ -338,7 +351,8 @@ export default class Radar {
|
|
|
338
351
|
|
|
339
352
|
this._prevTotalItems = totalItems;
|
|
340
353
|
this._prevFirstKey = totalItems > 0 ? keyForItem(firstItem, key, 0) : 0;
|
|
341
|
-
this._prevLastKey =
|
|
354
|
+
this._prevLastKey =
|
|
355
|
+
totalItems > 0 ? keyForItem(lastItem, key, totalItems - 1) : 0;
|
|
342
356
|
}
|
|
343
357
|
|
|
344
358
|
_updateConstants() {
|
|
@@ -346,57 +360,89 @@ export default class Radar {
|
|
|
346
360
|
estimateHeight,
|
|
347
361
|
_occludedContentBefore,
|
|
348
362
|
_itemContainer,
|
|
349
|
-
_scrollContainer
|
|
363
|
+
_scrollContainer,
|
|
350
364
|
} = this;
|
|
351
365
|
|
|
352
|
-
assert(
|
|
353
|
-
|
|
354
|
-
|
|
366
|
+
assert(
|
|
367
|
+
'Must provide a `estimateHeight` value to vertical-collection',
|
|
368
|
+
estimateHeight !== null,
|
|
369
|
+
);
|
|
370
|
+
assert(
|
|
371
|
+
'itemContainer must be set on Radar before scheduling an update',
|
|
372
|
+
_itemContainer !== null,
|
|
373
|
+
);
|
|
374
|
+
assert(
|
|
375
|
+
'scrollContainer must be set on Radar before scheduling an update',
|
|
376
|
+
_scrollContainer !== null,
|
|
377
|
+
);
|
|
355
378
|
|
|
356
379
|
// The scroll container's offsetHeight will reflect the actual height of the element, while
|
|
357
380
|
// it's measured height via bounding client rect will reflect the height with any transformations
|
|
358
381
|
// applied. We use this to find out the scale of the items so we can store measurements at the
|
|
359
382
|
// correct heights.
|
|
360
383
|
const scrollContainerOffsetHeight = _scrollContainer.offsetHeight;
|
|
361
|
-
const { height: scrollContainerRenderedHeight } =
|
|
384
|
+
const { height: scrollContainerRenderedHeight } =
|
|
385
|
+
_scrollContainer.getBoundingClientRect();
|
|
362
386
|
|
|
363
387
|
let transformScale;
|
|
364
388
|
|
|
365
389
|
// transformScale represents the opposite of the scale, if any, applied to the collection. Check for equality
|
|
366
390
|
// to guard against floating point errors, and check to make sure we're not dividing by zero (default to scale 1 if so)
|
|
367
|
-
if (
|
|
391
|
+
if (
|
|
392
|
+
scrollContainerOffsetHeight === scrollContainerRenderedHeight ||
|
|
393
|
+
scrollContainerRenderedHeight === 0
|
|
394
|
+
) {
|
|
368
395
|
transformScale = 1;
|
|
369
396
|
} else {
|
|
370
|
-
transformScale =
|
|
397
|
+
transformScale =
|
|
398
|
+
scrollContainerOffsetHeight / scrollContainerRenderedHeight;
|
|
371
399
|
}
|
|
372
400
|
|
|
373
|
-
const { top: scrollContentTop } = getScaledClientRect(
|
|
374
|
-
|
|
401
|
+
const { top: scrollContentTop } = getScaledClientRect(
|
|
402
|
+
_occludedContentBefore,
|
|
403
|
+
transformScale,
|
|
404
|
+
);
|
|
405
|
+
const { top: scrollContainerTop } = getScaledClientRect(
|
|
406
|
+
_scrollContainer,
|
|
407
|
+
transformScale,
|
|
408
|
+
);
|
|
375
409
|
|
|
376
410
|
let scrollContainerMaxHeight = 0;
|
|
377
411
|
|
|
378
412
|
if (_scrollContainer instanceof Element) {
|
|
379
|
-
const maxHeightStyle =
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
413
|
+
const maxHeightStyle =
|
|
414
|
+
window.getComputedStyle(_scrollContainer).maxHeight;
|
|
415
|
+
|
|
416
|
+
if (maxHeightStyle && maxHeightStyle !== 'none') {
|
|
417
|
+
scrollContainerMaxHeight = estimateElementHeight(
|
|
418
|
+
_scrollContainer.parentElement,
|
|
419
|
+
maxHeightStyle,
|
|
420
|
+
);
|
|
383
421
|
}
|
|
384
422
|
}
|
|
385
423
|
|
|
386
|
-
const calculatedEstimateHeight =
|
|
387
|
-
|
|
388
|
-
|
|
424
|
+
const calculatedEstimateHeight =
|
|
425
|
+
typeof estimateHeight === 'string' && estimateHeight
|
|
426
|
+
? estimateElementHeight(_itemContainer, estimateHeight)
|
|
427
|
+
: estimateHeight;
|
|
389
428
|
|
|
390
|
-
assert(
|
|
429
|
+
assert(
|
|
430
|
+
`calculatedEstimateHeight must be greater than 0, instead was "${calculatedEstimateHeight}" based on estimateHeight: ${estimateHeight}`,
|
|
431
|
+
calculatedEstimateHeight > 0,
|
|
432
|
+
);
|
|
391
433
|
|
|
392
434
|
this._transformScale = transformScale;
|
|
393
435
|
this._calculatedEstimateHeight = calculatedEstimateHeight;
|
|
394
|
-
this._calculatedScrollContainerHeight = roundTo(
|
|
436
|
+
this._calculatedScrollContainerHeight = roundTo(
|
|
437
|
+
Math.max(scrollContainerOffsetHeight, scrollContainerMaxHeight),
|
|
438
|
+
);
|
|
395
439
|
|
|
396
440
|
// The offset between the top of the collection and the top of the scroll container. Determined by finding
|
|
397
441
|
// the distance from the collection is from the top of the scroll container's content (scrollTop + actual position)
|
|
398
442
|
// and subtracting the scroll containers actual top.
|
|
399
|
-
this._collectionOffset = roundTo(
|
|
443
|
+
this._collectionOffset = roundTo(
|
|
444
|
+
_scrollContainer.scrollTop + scrollContentTop - scrollContainerTop,
|
|
445
|
+
);
|
|
400
446
|
}
|
|
401
447
|
|
|
402
448
|
/*
|
|
@@ -433,10 +479,13 @@ export default class Radar {
|
|
|
433
479
|
|
|
434
480
|
_occludedContentBefore,
|
|
435
481
|
_occludedContentAfter,
|
|
436
|
-
totalItems
|
|
482
|
+
totalItems,
|
|
437
483
|
} = this;
|
|
438
484
|
|
|
439
|
-
let renderedFirstItemIndex,
|
|
485
|
+
let renderedFirstItemIndex,
|
|
486
|
+
renderedLastItemIndex,
|
|
487
|
+
renderedTotalBefore,
|
|
488
|
+
renderedTotalAfter;
|
|
440
489
|
|
|
441
490
|
if (renderAll === true) {
|
|
442
491
|
// All items should be rendered, set indexes based on total item count
|
|
@@ -444,31 +493,35 @@ export default class Radar {
|
|
|
444
493
|
renderedLastItemIndex = totalItems - 1;
|
|
445
494
|
renderedTotalBefore = 0;
|
|
446
495
|
renderedTotalAfter = 0;
|
|
447
|
-
|
|
448
496
|
} else if (_started === false) {
|
|
449
497
|
// The Radar hasn't been started yet, render the initialRenderCount if it exists
|
|
450
498
|
renderedFirstItemIndex = this.startingIndex;
|
|
451
499
|
renderedLastItemIndex = this.startingIndex + this.initialRenderCount - 1;
|
|
452
500
|
renderedTotalBefore = 0;
|
|
453
501
|
renderedTotalAfter = 0;
|
|
454
|
-
|
|
455
502
|
} else {
|
|
456
503
|
renderedFirstItemIndex = this.firstItemIndex;
|
|
457
504
|
renderedLastItemIndex = this.lastItemIndex;
|
|
458
505
|
renderedTotalBefore = this.totalBefore;
|
|
459
506
|
renderedTotalAfter = this.totalAfter;
|
|
460
|
-
|
|
461
507
|
}
|
|
462
508
|
|
|
463
509
|
// If there are less items available than rendered, we drop the last rendered item index
|
|
464
510
|
renderedLastItemIndex = Math.min(renderedLastItemIndex, totalItems - 1);
|
|
465
511
|
|
|
466
512
|
// Add components to be recycled to the pool
|
|
467
|
-
while (
|
|
513
|
+
while (
|
|
514
|
+
orderedComponents.length > 0 &&
|
|
515
|
+
orderedComponents[0].index < renderedFirstItemIndex
|
|
516
|
+
) {
|
|
468
517
|
_componentPool.push(orderedComponents.shift());
|
|
469
518
|
}
|
|
470
519
|
|
|
471
|
-
while (
|
|
520
|
+
while (
|
|
521
|
+
orderedComponents.length > 0 &&
|
|
522
|
+
orderedComponents[orderedComponents.length - 1].index >
|
|
523
|
+
renderedLastItemIndex
|
|
524
|
+
) {
|
|
472
525
|
_componentPool.unshift(orderedComponents.pop());
|
|
473
526
|
}
|
|
474
527
|
|
|
@@ -488,8 +541,14 @@ export default class Radar {
|
|
|
488
541
|
}
|
|
489
542
|
}
|
|
490
543
|
|
|
491
|
-
let firstIndexInList =
|
|
492
|
-
|
|
544
|
+
let firstIndexInList =
|
|
545
|
+
orderedComponents.length > 0
|
|
546
|
+
? orderedComponents[0].index
|
|
547
|
+
: renderedFirstItemIndex;
|
|
548
|
+
let lastIndexInList =
|
|
549
|
+
orderedComponents.length > 0
|
|
550
|
+
? orderedComponents[orderedComponents.length - 1].index
|
|
551
|
+
: renderedFirstItemIndex - 1;
|
|
493
552
|
|
|
494
553
|
// Append as many items as needed to the rendered components
|
|
495
554
|
while (lastIndexInList < renderedLastItemIndex) {
|
|
@@ -529,28 +588,33 @@ export default class Radar {
|
|
|
529
588
|
|
|
530
589
|
// If there are any items remaining in the pool, remove them
|
|
531
590
|
if (_componentPool.length > 0) {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
591
|
+
// Grab the DOM of the remaining components and move it to temporary node disconnected from
|
|
592
|
+
// the body if the item can be reused later otherwise delete the component to avoid virtual re-rendering of the
|
|
593
|
+
// deleted item. If we end up using these components again, we'll grab their DOM and put it back
|
|
594
|
+
for (let i = _componentPool.length - 1; i >= 0; i--) {
|
|
595
|
+
const component = _componentPool[i];
|
|
596
|
+
const item = objectAt(items, component.index);
|
|
597
|
+
if (shouldRecycle === true && item) {
|
|
598
|
+
insertRangeBefore(
|
|
599
|
+
this._domPool,
|
|
600
|
+
null,
|
|
601
|
+
component.realUpperBound,
|
|
602
|
+
component.realLowerBound,
|
|
603
|
+
);
|
|
604
|
+
} else {
|
|
605
|
+
// Insert the virtual component bound back to make sure Glimmer is
|
|
606
|
+
// not confused about the state of the DOM.
|
|
607
|
+
insertRangeBefore(
|
|
608
|
+
this._itemContainer,
|
|
609
|
+
null,
|
|
610
|
+
component.realUpperBound,
|
|
611
|
+
component.realLowerBound,
|
|
612
|
+
);
|
|
613
|
+
run(() => {
|
|
614
|
+
virtualComponents.removeObject(component);
|
|
615
|
+
});
|
|
616
|
+
_componentPool.splice(i, 1);
|
|
550
617
|
}
|
|
551
|
-
} else {
|
|
552
|
-
virtualComponents.removeObjects(_componentPool);
|
|
553
|
-
_componentPool.length = 0;
|
|
554
618
|
}
|
|
555
619
|
}
|
|
556
620
|
|
|
@@ -562,10 +626,16 @@ export default class Radar {
|
|
|
562
626
|
|
|
563
627
|
// Set padding element heights.
|
|
564
628
|
_occludedContentBefore.style.height = `${Math.max(renderedTotalBefore, 0)}px`;
|
|
565
|
-
_occludedContentBefore.innerHTML =
|
|
629
|
+
_occludedContentBefore.innerHTML =
|
|
630
|
+
totalItemsBefore > 0
|
|
631
|
+
? `And ${totalItemsBefore} ${beforeItemsText} before`
|
|
632
|
+
: '';
|
|
566
633
|
|
|
567
634
|
_occludedContentAfter.style.height = `${Math.max(renderedTotalAfter, 0)}px`;
|
|
568
|
-
_occludedContentAfter.innerHTML =
|
|
635
|
+
_occludedContentAfter.innerHTML =
|
|
636
|
+
totalItemsAfter > 0
|
|
637
|
+
? `And ${totalItemsAfter} ${afterItemsText} after`
|
|
638
|
+
: '';
|
|
569
639
|
}
|
|
570
640
|
|
|
571
641
|
_appendComponent(component) {
|
|
@@ -574,22 +644,27 @@ export default class Radar {
|
|
|
574
644
|
_occludedContentAfter,
|
|
575
645
|
_appendComponentPool,
|
|
576
646
|
shouldRecycle,
|
|
577
|
-
_itemContainer
|
|
647
|
+
_itemContainer,
|
|
578
648
|
} = this;
|
|
579
649
|
|
|
580
650
|
const relativeNode = _occludedContentAfter.realUpperBound;
|
|
581
651
|
|
|
582
652
|
if (component.rendered === true) {
|
|
583
|
-
insertRangeBefore(
|
|
653
|
+
insertRangeBefore(
|
|
654
|
+
_itemContainer,
|
|
655
|
+
relativeNode,
|
|
656
|
+
component.realUpperBound,
|
|
657
|
+
component.realLowerBound,
|
|
658
|
+
);
|
|
584
659
|
} else {
|
|
585
660
|
virtualComponents.insertAt(virtualComponents.length - 1, component);
|
|
586
661
|
component.rendered = true;
|
|
587
662
|
|
|
588
|
-
// shouldRecycle=false breaks UI when scrolling the elements fast.
|
|
663
|
+
// shouldRecycle=false breaks UI when scrolling the elements fast.
|
|
589
664
|
// Reference https://github.com/html-next/vertical-collection/issues/296
|
|
590
665
|
// Components that are both new and appended still need to be rendered at the end because Glimmer.
|
|
591
666
|
// We have to move them _after_ they render, so we schedule that if they exist
|
|
592
|
-
if(!shouldRecycle) {
|
|
667
|
+
if (!shouldRecycle) {
|
|
593
668
|
_appendComponentPool.unshift(component);
|
|
594
669
|
|
|
595
670
|
if (this._nextLayout === null) {
|
|
@@ -602,7 +677,12 @@ export default class Radar {
|
|
|
602
677
|
// Changes with each inserted component
|
|
603
678
|
const relativeNode = _occludedContentAfter.realUpperBound;
|
|
604
679
|
|
|
605
|
-
insertRangeBefore(
|
|
680
|
+
insertRangeBefore(
|
|
681
|
+
this._itemContainer,
|
|
682
|
+
relativeNode,
|
|
683
|
+
component.realUpperBound,
|
|
684
|
+
component.realLowerBound,
|
|
685
|
+
);
|
|
606
686
|
}
|
|
607
687
|
});
|
|
608
688
|
}
|
|
@@ -615,13 +695,18 @@ export default class Radar {
|
|
|
615
695
|
virtualComponents,
|
|
616
696
|
_occludedContentBefore,
|
|
617
697
|
_prependComponentPool,
|
|
618
|
-
_itemContainer
|
|
698
|
+
_itemContainer,
|
|
619
699
|
} = this;
|
|
620
700
|
|
|
621
701
|
const relativeNode = _occludedContentBefore.realLowerBound.nextSibling;
|
|
622
702
|
|
|
623
703
|
if (component.rendered === true) {
|
|
624
|
-
insertRangeBefore(
|
|
704
|
+
insertRangeBefore(
|
|
705
|
+
_itemContainer,
|
|
706
|
+
relativeNode,
|
|
707
|
+
component.realUpperBound,
|
|
708
|
+
component.realLowerBound,
|
|
709
|
+
);
|
|
625
710
|
} else {
|
|
626
711
|
virtualComponents.insertAt(virtualComponents.length - 1, component);
|
|
627
712
|
component.rendered = true;
|
|
@@ -638,9 +723,15 @@ export default class Radar {
|
|
|
638
723
|
const component = _prependComponentPool.pop();
|
|
639
724
|
|
|
640
725
|
// Changes with each inserted component
|
|
641
|
-
const relativeNode =
|
|
642
|
-
|
|
643
|
-
|
|
726
|
+
const relativeNode =
|
|
727
|
+
_occludedContentBefore.realLowerBound.nextSibling;
|
|
728
|
+
|
|
729
|
+
insertRangeBefore(
|
|
730
|
+
_itemContainer,
|
|
731
|
+
relativeNode,
|
|
732
|
+
component.realUpperBound,
|
|
733
|
+
component.realLowerBound,
|
|
734
|
+
);
|
|
644
735
|
}
|
|
645
736
|
});
|
|
646
737
|
}
|
|
@@ -661,7 +752,7 @@ export default class Radar {
|
|
|
661
752
|
|
|
662
753
|
_firstReached,
|
|
663
754
|
_lastReached,
|
|
664
|
-
_didReset
|
|
755
|
+
_didReset,
|
|
665
756
|
} = this;
|
|
666
757
|
|
|
667
758
|
if (_didReset || firstVisibleIndex !== _prevFirstVisibleIndex) {
|
|
@@ -687,7 +778,9 @@ export default class Radar {
|
|
|
687
778
|
this._prevFirstItemIndex += numPrepended;
|
|
688
779
|
this._prevLastItemIndex += numPrepended;
|
|
689
780
|
|
|
690
|
-
this.orderedComponents.forEach((c) =>
|
|
781
|
+
this.orderedComponents.forEach((c) =>
|
|
782
|
+
set(c, 'index', get(c, 'index') + numPrepended),
|
|
783
|
+
);
|
|
691
784
|
|
|
692
785
|
this._firstReached = false;
|
|
693
786
|
|
|
@@ -709,14 +802,13 @@ export default class Radar {
|
|
|
709
802
|
return; // All items rendered, no need to page up
|
|
710
803
|
}
|
|
711
804
|
|
|
712
|
-
const {
|
|
713
|
-
bufferSize,
|
|
714
|
-
firstItemIndex,
|
|
715
|
-
totalComponents
|
|
716
|
-
} = this;
|
|
805
|
+
const { bufferSize, firstItemIndex, totalComponents } = this;
|
|
717
806
|
|
|
718
807
|
if (firstItemIndex !== 0) {
|
|
719
|
-
const newFirstItemIndex = Math.max(
|
|
808
|
+
const newFirstItemIndex = Math.max(
|
|
809
|
+
firstItemIndex - totalComponents + bufferSize,
|
|
810
|
+
0,
|
|
811
|
+
);
|
|
720
812
|
const offset = this.getOffsetForIndex(newFirstItemIndex);
|
|
721
813
|
|
|
722
814
|
this._scrollContainer.scrollTop = offset + this._collectionOffset;
|
|
@@ -729,15 +821,13 @@ export default class Radar {
|
|
|
729
821
|
return; // All items rendered, no need to page down
|
|
730
822
|
}
|
|
731
823
|
|
|
732
|
-
const {
|
|
733
|
-
bufferSize,
|
|
734
|
-
lastItemIndex,
|
|
735
|
-
totalComponents,
|
|
736
|
-
totalItems
|
|
737
|
-
} = this;
|
|
824
|
+
const { bufferSize, lastItemIndex, totalComponents, totalItems } = this;
|
|
738
825
|
|
|
739
826
|
if (lastItemIndex !== totalItems - 1) {
|
|
740
|
-
const newFirstItemIndex = Math.min(
|
|
827
|
+
const newFirstItemIndex = Math.min(
|
|
828
|
+
lastItemIndex + bufferSize + 1,
|
|
829
|
+
totalItems - totalComponents,
|
|
830
|
+
);
|
|
741
831
|
const offset = this.getOffsetForIndex(newFirstItemIndex);
|
|
742
832
|
|
|
743
833
|
this._scrollContainer.scrollTop = offset + this._collectionOffset;
|
|
@@ -746,7 +836,10 @@ export default class Radar {
|
|
|
746
836
|
}
|
|
747
837
|
|
|
748
838
|
get totalComponents() {
|
|
749
|
-
return Math.min(
|
|
839
|
+
return Math.min(
|
|
840
|
+
this.totalItems,
|
|
841
|
+
this.lastItemIndex - this.firstItemIndex + 1,
|
|
842
|
+
);
|
|
750
843
|
}
|
|
751
844
|
|
|
752
845
|
/*
|
|
@@ -763,18 +856,24 @@ export default class Radar {
|
|
|
763
856
|
* in this exact order.
|
|
764
857
|
*/
|
|
765
858
|
get visibleTop() {
|
|
766
|
-
return Math.max(
|
|
859
|
+
return Math.max(
|
|
860
|
+
this._scrollTop - this._collectionOffset + this._prependOffset,
|
|
861
|
+
0,
|
|
862
|
+
);
|
|
767
863
|
}
|
|
768
864
|
|
|
769
865
|
get visibleMiddle() {
|
|
770
|
-
return this.visibleTop +
|
|
866
|
+
return this.visibleTop + this._calculatedScrollContainerHeight / 2;
|
|
771
867
|
}
|
|
772
868
|
|
|
773
869
|
get visibleBottom() {
|
|
774
870
|
// There is a case where the container of this vertical collection could have height 0 at
|
|
775
871
|
// initial render step but will be updated later. We want to return visibleBottom to be 0 rather
|
|
776
872
|
// than -1.
|
|
777
|
-
return Math.max(
|
|
873
|
+
return Math.max(
|
|
874
|
+
this.visibleTop + this._calculatedScrollContainerHeight - 1,
|
|
875
|
+
0,
|
|
876
|
+
);
|
|
778
877
|
}
|
|
779
878
|
|
|
780
879
|
get totalItems() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEBUG } from '@glimmer/env';
|
|
2
2
|
|
|
3
|
-
import Radar from './radar';
|
|
3
|
+
import Radar from './radar.js';
|
|
4
4
|
|
|
5
5
|
export default class StaticRadar extends Radar {
|
|
6
6
|
constructor(parentToken, options) {
|
|
@@ -20,7 +20,7 @@ export default class StaticRadar extends Radar {
|
|
|
20
20
|
totalItems,
|
|
21
21
|
visibleMiddle,
|
|
22
22
|
_calculatedEstimateHeight,
|
|
23
|
-
_calculatedScrollContainerHeight
|
|
23
|
+
_calculatedScrollContainerHeight,
|
|
24
24
|
} = this;
|
|
25
25
|
|
|
26
26
|
if (totalItems === 0) {
|
|
@@ -32,9 +32,14 @@ export default class StaticRadar extends Radar {
|
|
|
32
32
|
|
|
33
33
|
const maxIndex = totalItems - 1;
|
|
34
34
|
|
|
35
|
-
const middleItemIndex = Math.floor(
|
|
35
|
+
const middleItemIndex = Math.floor(
|
|
36
|
+
visibleMiddle / _calculatedEstimateHeight,
|
|
37
|
+
);
|
|
36
38
|
|
|
37
|
-
const shouldRenderCount = Math.min(
|
|
39
|
+
const shouldRenderCount = Math.min(
|
|
40
|
+
Math.ceil(_calculatedScrollContainerHeight / _calculatedEstimateHeight),
|
|
41
|
+
totalItems,
|
|
42
|
+
);
|
|
38
43
|
|
|
39
44
|
let firstItemIndex = middleItemIndex - Math.floor(shouldRenderCount / 2);
|
|
40
45
|
let lastItemIndex = middleItemIndex + Math.ceil(shouldRenderCount / 2) - 1;
|
|
@@ -57,7 +62,7 @@ export default class StaticRadar extends Radar {
|
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
_didEarthquake(scrollDiff) {
|
|
60
|
-
return scrollDiff >
|
|
65
|
+
return scrollDiff > this._calculatedEstimateHeight / 2;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
get total() {
|
|
@@ -69,7 +74,9 @@ export default class StaticRadar extends Radar {
|
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
get totalAfter() {
|
|
72
|
-
return
|
|
77
|
+
return (
|
|
78
|
+
this.total - (this.lastItemIndex + 1) * this._calculatedEstimateHeight
|
|
79
|
+
);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
get firstItemIndex() {
|
|
@@ -85,7 +92,12 @@ export default class StaticRadar extends Radar {
|
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
get lastVisibleIndex() {
|
|
88
|
-
return
|
|
95
|
+
return (
|
|
96
|
+
Math.min(
|
|
97
|
+
Math.ceil(this.visibleBottom / this._calculatedEstimateHeight),
|
|
98
|
+
this.totalItems,
|
|
99
|
+
) - 1
|
|
100
|
+
);
|
|
89
101
|
}
|
|
90
102
|
|
|
91
103
|
/*
|