@html-next/vertical-collection 1.0.0-beta.9 → 2.0.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/.github/workflows/ci.yml +76 -0
- package/CHANGELOG.md +13 -0
- package/README.md +30 -48
- package/RELEASE.md +60 -0
- package/addon/-debug/edge-visualization/debug-mixin.js +5 -9
- package/addon/-debug/edge-visualization/visualization.js +2 -2
- package/addon/-private/data-view/elements/occluded-content.js +84 -0
- package/addon/-private/data-view/{container.js → elements/viewport-container.js} +5 -5
- package/addon/-private/data-view/{virtual-component.js → elements/virtual-component.js} +15 -13
- package/addon/-private/data-view/radar/dynamic-radar.js +4 -5
- package/addon/-private/data-view/radar/radar.js +90 -31
- package/addon/-private/data-view/utils/mutation-checkers.js +29 -0
- package/addon/-private/data-view/utils/scroll-handler.js +1 -3
- package/addon/-private/data-view/utils/supports-passive.js +1 -0
- package/addon/-private/data-view/viewport-container.js +70 -0
- package/addon/-private/ember-internals/identity.js +1 -5
- package/addon/-private/ember-internals/key-for-item.js +1 -5
- package/addon/-private/index.js +1 -1
- package/addon/-private/utils/document-shim.js +1 -0
- package/addon/components/vertical-collection/component.js +31 -84
- package/addon/components/vertical-collection/template.hbs +2 -2
- package/addon/styles/app.css +7 -6
- package/app/components/vertical-collection.js +1 -1
- package/app/initializers/debug.js +1 -1
- package/config/ember-cli-toolbelts.json +1 -5
- package/config/environment.js +0 -1
- package/index.js +45 -41
- package/package.json +96 -64
- package/.npmignore +0 -16
- package/app/initializers/vertical-collection-legacy-compat.js +0 -14
- package/config/changelog.js +0 -23
- package/config/release.js +0 -19
- package/yarn.lock +0 -6533
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { A } from '@ember/array';
|
|
2
|
+
import { set, get } from '@ember/object';
|
|
2
3
|
import { assert } from '@ember/debug';
|
|
3
4
|
import { DEBUG } from '@glimmer/env';
|
|
4
5
|
|
|
5
6
|
import { Token, scheduler } from 'ember-raf-scheduler';
|
|
6
7
|
|
|
7
|
-
import VirtualComponent from '../virtual-component';
|
|
8
|
+
import VirtualComponent from '../elements/virtual-component';
|
|
9
|
+
import OccludedContent from '../elements/occluded-content';
|
|
8
10
|
import insertRangeBefore from '../utils/insert-range-before';
|
|
9
11
|
import objectAt from '../utils/object-at';
|
|
10
12
|
import roundTo from '../utils/round-to';
|
|
13
|
+
import { isPrepend, isAppend } from '../utils/mutation-checkers';
|
|
11
14
|
|
|
12
15
|
import {
|
|
13
16
|
addScrollHandler,
|
|
14
17
|
removeScrollHandler
|
|
15
18
|
} from '../utils/scroll-handler';
|
|
16
19
|
|
|
17
|
-
import
|
|
20
|
+
import ViewportContainer from '../viewport-container';
|
|
18
21
|
|
|
19
22
|
import closestElement from '../../utils/element/closest';
|
|
20
23
|
import estimateElementHeight from '../../utils/element/estimate-element-height';
|
|
21
24
|
import getScaledClientRect from '../../utils/element/get-scaled-client-rect';
|
|
25
|
+
import keyForItem from '../../ember-internals/key-for-item';
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
A,
|
|
25
|
-
get,
|
|
26
|
-
set
|
|
27
|
-
} = Ember;
|
|
27
|
+
import document from '../../utils/document-shim';
|
|
28
28
|
|
|
29
29
|
export default class Radar {
|
|
30
30
|
constructor(
|
|
@@ -33,12 +33,14 @@ export default class Radar {
|
|
|
33
33
|
bufferSize,
|
|
34
34
|
containerSelector,
|
|
35
35
|
estimateHeight,
|
|
36
|
+
initialRenderCount,
|
|
36
37
|
items,
|
|
38
|
+
key,
|
|
37
39
|
renderAll,
|
|
38
40
|
renderFromLast,
|
|
39
|
-
initialRenderCount,
|
|
40
41
|
shouldRecycle,
|
|
41
|
-
startingIndex
|
|
42
|
+
startingIndex,
|
|
43
|
+
occlusionTagName
|
|
42
44
|
}
|
|
43
45
|
) {
|
|
44
46
|
this.token = new Token(parentToken);
|
|
@@ -49,6 +51,7 @@ export default class Radar {
|
|
|
49
51
|
this.estimateHeight = estimateHeight;
|
|
50
52
|
this.initialRenderCount = initialRenderCount;
|
|
51
53
|
this.items = items;
|
|
54
|
+
this.key = key;
|
|
52
55
|
this.renderAll = renderAll;
|
|
53
56
|
this.renderFromLast = renderFromLast;
|
|
54
57
|
this.shouldRecycle = shouldRecycle;
|
|
@@ -82,32 +85,40 @@ export default class Radar {
|
|
|
82
85
|
this._nextLayout = null;
|
|
83
86
|
this._started = false;
|
|
84
87
|
this._didReset = true;
|
|
88
|
+
this._didUpdateItems = false;
|
|
85
89
|
|
|
86
90
|
// Cache state
|
|
87
91
|
this._scrollTop = 0;
|
|
92
|
+
|
|
88
93
|
// Setting these values to infinity starts us in a guaranteed good state for the radar,
|
|
89
94
|
// so it knows that it needs to run certain measurements, etc.
|
|
90
95
|
this._prevFirstItemIndex = Infinity;
|
|
91
96
|
this._prevLastItemIndex = -Infinity;
|
|
92
97
|
this._prevFirstVisibleIndex = 0;
|
|
93
98
|
this._prevLastVisibleIndex = 0;
|
|
99
|
+
|
|
94
100
|
this._firstReached = false;
|
|
95
101
|
this._lastReached = false;
|
|
102
|
+
this._prevTotalItems = 0;
|
|
103
|
+
this._prevFirstKey = 0;
|
|
104
|
+
this._prevLastKey = 0;
|
|
105
|
+
|
|
96
106
|
this._componentPool = [];
|
|
97
107
|
this._prependComponentPool = [];
|
|
98
108
|
|
|
99
109
|
// Boundaries
|
|
100
|
-
this._occludedContentBefore = new
|
|
101
|
-
this._occludedContentAfter = new
|
|
102
|
-
|
|
103
|
-
this._occludedContentBefore.element = document.createElement('occluded-content');
|
|
104
|
-
this._occludedContentAfter.element = document.createElement('occluded-content');
|
|
110
|
+
this._occludedContentBefore = new OccludedContent(occlusionTagName);
|
|
111
|
+
this._occludedContentAfter = new OccludedContent(occlusionTagName);
|
|
105
112
|
|
|
106
|
-
this.
|
|
107
|
-
this.
|
|
113
|
+
this._pageUpHandler = this.pageUp.bind(this);
|
|
114
|
+
this._occludedContentBefore.addEventListener('click', this._pageUpHandler);
|
|
115
|
+
this._pageDownHandler = this.pageDown.bind(this);
|
|
116
|
+
this._occludedContentAfter.addEventListener('click', this._pageDownHandler);
|
|
108
117
|
|
|
109
118
|
// Element to hold pooled component DOM when not in use
|
|
110
|
-
|
|
119
|
+
if (document) {
|
|
120
|
+
this._domPool = document.createDocumentFragment();
|
|
121
|
+
}
|
|
111
122
|
|
|
112
123
|
// Initialize virtual components
|
|
113
124
|
this.virtualComponents = A([this._occludedContentBefore, this._occludedContentAfter]);
|
|
@@ -131,12 +142,18 @@ export default class Radar {
|
|
|
131
142
|
this.orderedComponents[i].destroy();
|
|
132
143
|
}
|
|
133
144
|
|
|
145
|
+
// Boundaries
|
|
146
|
+
this._occludedContentBefore.removeEventListener('click', this._pageUpHandler);
|
|
147
|
+
this._occludedContentAfter.removeEventListener('click', this._pageDownHandler);
|
|
148
|
+
this._occludedContentBefore.destroy();
|
|
149
|
+
this._occludedContentAfter.destroy();
|
|
150
|
+
|
|
134
151
|
this.orderedComponents = null;
|
|
135
152
|
set(this, 'virtualComponents', null);
|
|
136
153
|
|
|
137
154
|
if (this._started) {
|
|
138
155
|
removeScrollHandler(this._scrollContainer, this._scrollHandler);
|
|
139
|
-
|
|
156
|
+
ViewportContainer.removeEventListener('resize', this._resizeHandler);
|
|
140
157
|
}
|
|
141
158
|
}
|
|
142
159
|
|
|
@@ -158,7 +175,7 @@ export default class Radar {
|
|
|
158
175
|
// Use the occluded content element, which has been inserted into the DOM,
|
|
159
176
|
// to find the item container and the scroll container
|
|
160
177
|
this._itemContainer = _occludedContentBefore.element.parentNode;
|
|
161
|
-
this._scrollContainer = containerSelector === 'body' ?
|
|
178
|
+
this._scrollContainer = containerSelector === 'body' ? ViewportContainer : closestElement(this._itemContainer, containerSelector);
|
|
162
179
|
|
|
163
180
|
this._updateConstants();
|
|
164
181
|
|
|
@@ -180,6 +197,10 @@ export default class Radar {
|
|
|
180
197
|
// initialize the scrollTop value, which will be applied to the
|
|
181
198
|
// scrollContainer after the collection has been initialized
|
|
182
199
|
this._scrollTop = startingScrollTop + _collectionOffset;
|
|
200
|
+
|
|
201
|
+
this._prevFirstVisibleIndex = startingIndex;
|
|
202
|
+
} else {
|
|
203
|
+
this._scrollTop = this._scrollContainer.scrollTop;
|
|
183
204
|
}
|
|
184
205
|
|
|
185
206
|
this._started = true;
|
|
@@ -187,7 +208,7 @@ export default class Radar {
|
|
|
187
208
|
|
|
188
209
|
// Setup event handlers
|
|
189
210
|
addScrollHandler(this._scrollContainer, this._scrollHandler);
|
|
190
|
-
|
|
211
|
+
ViewportContainer.addEventListener('resize', this._resizeHandler);
|
|
191
212
|
}
|
|
192
213
|
|
|
193
214
|
/*
|
|
@@ -202,7 +223,13 @@ export default class Radar {
|
|
|
202
223
|
*
|
|
203
224
|
* @private
|
|
204
225
|
*/
|
|
205
|
-
scheduleUpdate() {
|
|
226
|
+
scheduleUpdate(didUpdateItems) {
|
|
227
|
+
if (didUpdateItems === true) {
|
|
228
|
+
// Set the update items flag first, in case scheduleUpdate has already been called
|
|
229
|
+
// but the RAF hasn't yet run
|
|
230
|
+
this._didUpdateItems = true;
|
|
231
|
+
}
|
|
232
|
+
|
|
206
233
|
if (this._nextUpdate !== null || this._started === false) {
|
|
207
234
|
return;
|
|
208
235
|
}
|
|
@@ -216,6 +243,11 @@ export default class Radar {
|
|
|
216
243
|
}
|
|
217
244
|
|
|
218
245
|
update() {
|
|
246
|
+
if (this._didUpdateItems === true) {
|
|
247
|
+
this._determineUpdateType();
|
|
248
|
+
this._didUpdateItems = false;
|
|
249
|
+
}
|
|
250
|
+
|
|
219
251
|
this._updateConstants();
|
|
220
252
|
this._updateIndexes();
|
|
221
253
|
this._updateVirtualComponents();
|
|
@@ -224,7 +256,7 @@ export default class Radar {
|
|
|
224
256
|
}
|
|
225
257
|
|
|
226
258
|
afterUpdate() {
|
|
227
|
-
const { totalItems } = this;
|
|
259
|
+
const { _prevTotalItems: totalItems } = this;
|
|
228
260
|
|
|
229
261
|
const scrollDiff = this._calculateScrollDiff();
|
|
230
262
|
|
|
@@ -238,7 +270,6 @@ export default class Radar {
|
|
|
238
270
|
// Unset prepend offset, we're done with any prepend changes at this point
|
|
239
271
|
this._prependOffset = 0;
|
|
240
272
|
|
|
241
|
-
// Send actions if there are any items
|
|
242
273
|
if (totalItems !== 0) {
|
|
243
274
|
this._sendActions();
|
|
244
275
|
}
|
|
@@ -274,6 +305,35 @@ export default class Radar {
|
|
|
274
305
|
return (this._prependOffset + this._scrollTop) - this._scrollContainer.scrollTop;
|
|
275
306
|
}
|
|
276
307
|
|
|
308
|
+
_determineUpdateType() {
|
|
309
|
+
const {
|
|
310
|
+
items,
|
|
311
|
+
key,
|
|
312
|
+
totalItems,
|
|
313
|
+
|
|
314
|
+
_prevTotalItems,
|
|
315
|
+
_prevFirstKey,
|
|
316
|
+
_prevLastKey
|
|
317
|
+
} = this;
|
|
318
|
+
|
|
319
|
+
const lenDiff = totalItems - _prevTotalItems;
|
|
320
|
+
|
|
321
|
+
if (isPrepend(lenDiff, items, key, _prevFirstKey, _prevLastKey) === true) {
|
|
322
|
+
this.prepend(lenDiff);
|
|
323
|
+
} else if (isAppend(lenDiff, items, key, _prevFirstKey, _prevLastKey) === true) {
|
|
324
|
+
this.append(lenDiff);
|
|
325
|
+
} else {
|
|
326
|
+
this.reset();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const firstItem = objectAt(this.items, 0);
|
|
330
|
+
const lastItem = objectAt(this.items, this.totalItems - 1);
|
|
331
|
+
|
|
332
|
+
this._prevTotalItems = totalItems;
|
|
333
|
+
this._prevFirstKey = totalItems > 0 ? keyForItem(firstItem, key, 0) : 0;
|
|
334
|
+
this._prevLastKey = totalItems > 0 ? keyForItem(lastItem, key, totalItems - 1) : 0;
|
|
335
|
+
}
|
|
336
|
+
|
|
277
337
|
_updateConstants() {
|
|
278
338
|
const {
|
|
279
339
|
estimateHeight,
|
|
@@ -361,9 +421,8 @@ export default class Radar {
|
|
|
361
421
|
|
|
362
422
|
shouldRecycle,
|
|
363
423
|
renderAll,
|
|
364
|
-
|
|
365
|
-
_didReset,
|
|
366
424
|
_started,
|
|
425
|
+
_didReset,
|
|
367
426
|
|
|
368
427
|
_occludedContentBefore,
|
|
369
428
|
_occludedContentAfter,
|
|
@@ -483,12 +542,12 @@ export default class Radar {
|
|
|
483
542
|
const beforeItemsText = totalItemsBefore === 1 ? 'item' : 'items';
|
|
484
543
|
const afterItemsText = totalItemsAfter === 1 ? 'item' : 'items';
|
|
485
544
|
|
|
486
|
-
// Set padding element heights
|
|
487
|
-
_occludedContentBefore.
|
|
488
|
-
_occludedContentBefore.
|
|
545
|
+
// Set padding element heights.
|
|
546
|
+
_occludedContentBefore.style.height = `${Math.max(renderedTotalBefore, 0)}px`;
|
|
547
|
+
_occludedContentBefore.innerHTML = totalItemsBefore > 0 ? `And ${totalItemsBefore} ${beforeItemsText} before` : '';
|
|
489
548
|
|
|
490
|
-
_occludedContentAfter.
|
|
491
|
-
_occludedContentAfter.
|
|
549
|
+
_occludedContentAfter.style.height = `${Math.max(renderedTotalAfter, 0)}px`;
|
|
550
|
+
_occludedContentAfter.innerHTML = totalItemsAfter > 0 ? `And ${totalItemsAfter} ${afterItemsText} after` : '';
|
|
492
551
|
}
|
|
493
552
|
|
|
494
553
|
_appendComponent(component) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { get } from '@ember/object';
|
|
2
|
+
import objectAt from './object-at';
|
|
3
|
+
import keyForItem from '../../ember-internals/key-for-item';
|
|
4
|
+
|
|
5
|
+
export function isPrepend(lenDiff, newItems, key, oldFirstKey, oldLastKey) {
|
|
6
|
+
const newItemsLength = get(newItems, 'length');
|
|
7
|
+
|
|
8
|
+
if (lenDiff <= 0 || lenDiff >= newItemsLength || newItemsLength === 0) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const newFirstKey = keyForItem(objectAt(newItems, lenDiff), key, lenDiff);
|
|
13
|
+
const newLastKey = keyForItem(objectAt(newItems, newItemsLength - 1), key, newItemsLength - 1);
|
|
14
|
+
|
|
15
|
+
return oldFirstKey === newFirstKey && oldLastKey === newLastKey;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isAppend(lenDiff, newItems, key, oldFirstKey, oldLastKey) {
|
|
19
|
+
const newItemsLength = get(newItems, 'length');
|
|
20
|
+
|
|
21
|
+
if (lenDiff <= 0 || lenDiff >= newItemsLength || newItemsLength === 0) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const newFirstKey = keyForItem(objectAt(newItems, 0), key, 0);
|
|
26
|
+
const newLastKey = keyForItem(objectAt(newItems, newItemsLength - lenDiff - 1), key, newItemsLength - lenDiff - 1);
|
|
27
|
+
|
|
28
|
+
return oldFirstKey === newFirstKey && oldLastKey === newLastKey;
|
|
29
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
+
import { run } from '@ember/runloop';
|
|
1
2
|
import { scheduler } from 'ember-raf-scheduler';
|
|
2
3
|
import SUPPORTS_PASSIVE from './supports-passive';
|
|
3
|
-
import Ember from 'ember';
|
|
4
|
-
|
|
5
|
-
const { run } = Ember;
|
|
6
4
|
const DEFAULT_ARRAY_SIZE = 10;
|
|
7
5
|
const UNDEFINED_VALUE = Object.create(null);
|
|
8
6
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* There are significant differences between browsers
|
|
3
|
+
* in how they implement "scroll" on document.body
|
|
4
|
+
*
|
|
5
|
+
* The only cross-browser listener for scroll on body
|
|
6
|
+
* is to listen on window with capture.
|
|
7
|
+
*
|
|
8
|
+
* They also implement different standards for how to
|
|
9
|
+
* access the scroll position.
|
|
10
|
+
*
|
|
11
|
+
* This singleton class provides a cross-browser way
|
|
12
|
+
* to access and set the scrollTop and scrollLeft properties.
|
|
13
|
+
*
|
|
14
|
+
*/
|
|
15
|
+
export function ViewportContainer() {
|
|
16
|
+
|
|
17
|
+
// A bug occurs in Chrome when we reload the browser at a lower
|
|
18
|
+
// scrollTop, window.scrollY becomes stuck on a single value.
|
|
19
|
+
Object.defineProperty(this, 'scrollTop', {
|
|
20
|
+
get() {
|
|
21
|
+
return document.body.scrollTop
|
|
22
|
+
|| document.documentElement.scrollTop;
|
|
23
|
+
},
|
|
24
|
+
set(v) {
|
|
25
|
+
return document.body.scrollTop = document.documentElement.scrollTop = v;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
Object.defineProperty(this, 'scrollLeft', {
|
|
30
|
+
get() {
|
|
31
|
+
return window.scrollX
|
|
32
|
+
|| window.pageXOffset
|
|
33
|
+
|| document.body.scrollLeft
|
|
34
|
+
|| document.documentElement.scrollLeft;
|
|
35
|
+
},
|
|
36
|
+
set(v) {
|
|
37
|
+
return window.scrollX
|
|
38
|
+
= window.pageXOffset
|
|
39
|
+
= document.body.scrollLeft
|
|
40
|
+
= document.documentElement.scrollLeft = v;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
Object.defineProperty(this, 'offsetHeight', {
|
|
45
|
+
get() {
|
|
46
|
+
return window.innerHeight;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
ViewportContainer.prototype.addEventListener = function addEventListener(event, handler, options) {
|
|
52
|
+
return window.addEventListener(event, handler, options);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
ViewportContainer.prototype.removeEventListener = function addEventListener(event, handler, options) {
|
|
56
|
+
return window.removeEventListener(event, handler, options);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
ViewportContainer.prototype.getBoundingClientRect = function getBoundingClientRect() {
|
|
60
|
+
return {
|
|
61
|
+
height: window.innerHeight,
|
|
62
|
+
width: window.innerWidth,
|
|
63
|
+
top: 0,
|
|
64
|
+
left: 0,
|
|
65
|
+
right: window.innerWidth,
|
|
66
|
+
bottom: window.innerHeight
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default new ViewportContainer();
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { get } from '@ember/object';
|
|
2
2
|
import { assert } from '@ember/debug';
|
|
3
3
|
|
|
4
4
|
import identity from './identity';
|
|
5
5
|
|
|
6
|
-
const {
|
|
7
|
-
get
|
|
8
|
-
} = Ember;
|
|
9
|
-
|
|
10
6
|
export default function keyForItem(item, keyPath, index) {
|
|
11
7
|
let key;
|
|
12
8
|
|
package/addon/-private/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { default as closestElement } from './utils/element/closest';
|
|
|
5
5
|
export { default as DynamicRadar } from './data-view/radar/dynamic-radar';
|
|
6
6
|
export { default as StaticRadar } from './data-view/radar/static-radar';
|
|
7
7
|
|
|
8
|
-
export { default as
|
|
8
|
+
export { default as ViewportContainer } from './data-view/viewport-container';
|
|
9
9
|
export { default as objectAt } from './data-view/utils/object-at';
|
|
10
10
|
|
|
11
11
|
export {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default window ? window.document : undefined;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { empty, readOnly } from '@ember/object/computed';
|
|
2
|
+
|
|
3
|
+
import Component from '@ember/component';
|
|
4
|
+
import { get, computed } from '@ember/object';
|
|
5
|
+
import { run } from '@ember/runloop';
|
|
3
6
|
import layout from './template';
|
|
4
7
|
|
|
5
8
|
import { scheduler, Token } from 'ember-raf-scheduler';
|
|
6
9
|
|
|
7
|
-
import { SUPPORTS_INVERSE_BLOCK } from 'ember-compatibility-helpers';
|
|
8
|
-
|
|
9
10
|
import {
|
|
10
11
|
keyForItem,
|
|
11
12
|
DynamicRadar,
|
|
@@ -13,13 +14,6 @@ import {
|
|
|
13
14
|
objectAt
|
|
14
15
|
} from '../../-private';
|
|
15
16
|
|
|
16
|
-
const {
|
|
17
|
-
computed,
|
|
18
|
-
Component,
|
|
19
|
-
get,
|
|
20
|
-
run
|
|
21
|
-
} = Ember;
|
|
22
|
-
|
|
23
17
|
const VerticalCollection = Component.extend({
|
|
24
18
|
layout,
|
|
25
19
|
|
|
@@ -147,52 +141,28 @@ const VerticalCollection = Component.extend({
|
|
|
147
141
|
*/
|
|
148
142
|
renderAll: false,
|
|
149
143
|
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
/**
|
|
145
|
+
* The tag name used in DOM elements before and after the rendered list. By default, it is set to
|
|
146
|
+
* 'occluded-content' to avoid any confusion with user's CSS settings. However, it could be
|
|
147
|
+
* overriden to provide custom behavior (for example, in table user wants to set it to 'tr' to
|
|
148
|
+
* comply with table semantics).
|
|
149
|
+
*/
|
|
150
|
+
occlusionTagName: 'occluded-content',
|
|
151
|
+
|
|
152
|
+
isEmpty: empty('items'),
|
|
153
|
+
shouldYieldToInverse: readOnly('isEmpty'),
|
|
152
154
|
|
|
153
155
|
virtualComponents: computed('items.[]', 'renderAll', 'estimateHeight', 'bufferSize', function() {
|
|
154
|
-
const {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
_prevFirstKey,
|
|
158
|
-
_prevLastKey
|
|
159
|
-
} = this;
|
|
156
|
+
const { _radar } = this;
|
|
157
|
+
|
|
158
|
+
const items = this.get('items');
|
|
160
159
|
|
|
160
|
+
_radar.items = items === null || items === undefined ? [] : items;
|
|
161
161
|
_radar.estimateHeight = this.get('estimateHeight');
|
|
162
162
|
_radar.renderAll = this.get('renderAll');
|
|
163
163
|
_radar.bufferSize = this.get('bufferSize');
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
const itemsLength = get(items, 'length');
|
|
167
|
-
|
|
168
|
-
if (items === null || items === undefined || itemsLength === 0) {
|
|
169
|
-
_radar.items = [];
|
|
170
|
-
_radar.reset();
|
|
171
|
-
_radar.scheduleUpdate();
|
|
172
|
-
|
|
173
|
-
this._prevItemsLength = this._prevFirstKey = this._prevLastKey = 0;
|
|
174
|
-
|
|
175
|
-
return _radar.virtualComponents;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
_radar.items = items;
|
|
179
|
-
|
|
180
|
-
const key = this.get('key');
|
|
181
|
-
const lenDiff = itemsLength - _prevItemsLength;
|
|
182
|
-
|
|
183
|
-
this._prevItemsLength = itemsLength;
|
|
184
|
-
this._prevFirstKey = keyForItem(objectAt(items, 0), key, 0);
|
|
185
|
-
this._prevLastKey = keyForItem(objectAt(items, itemsLength - 1), key, itemsLength - 1);
|
|
186
|
-
|
|
187
|
-
if (isPrepend(lenDiff, items, key, _prevFirstKey, _prevLastKey) === true) {
|
|
188
|
-
_radar.prepend(lenDiff);
|
|
189
|
-
} else if (isAppend(lenDiff, items, key, _prevFirstKey, _prevLastKey) === true) {
|
|
190
|
-
_radar.append(lenDiff);
|
|
191
|
-
} else {
|
|
192
|
-
_radar.reset();
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
_radar.scheduleUpdate();
|
|
165
|
+
_radar.scheduleUpdate(true);
|
|
196
166
|
|
|
197
167
|
return _radar.virtualComponents;
|
|
198
168
|
}),
|
|
@@ -216,7 +186,13 @@ const VerticalCollection = Component.extend({
|
|
|
216
186
|
const item = objectAt(items, index);
|
|
217
187
|
const key = keyForItem(item, keyPath, index);
|
|
218
188
|
|
|
219
|
-
this.sendAction
|
|
189
|
+
// this.sendAction will be deprecated in ember 4.0
|
|
190
|
+
const _action = get(this, action);
|
|
191
|
+
if (typeof _action == 'function') {
|
|
192
|
+
_action(item, index, key);
|
|
193
|
+
} else if (typeof _action === 'string') {
|
|
194
|
+
this.sendAction(action, item, index, key);
|
|
195
|
+
}
|
|
220
196
|
});
|
|
221
197
|
this._scheduledActions.length = 0;
|
|
222
198
|
});
|
|
@@ -252,6 +228,7 @@ const VerticalCollection = Component.extend({
|
|
|
252
228
|
const renderAll = this.get('renderAll');
|
|
253
229
|
const renderFromLast = this.get('renderFromLast');
|
|
254
230
|
const shouldRecycle = this.get('shouldRecycle');
|
|
231
|
+
const occlusionTagName = this.get('occlusionTagName');
|
|
255
232
|
|
|
256
233
|
const idForFirstItem = this.get('idForFirstItem');
|
|
257
234
|
const key = this.get('key');
|
|
@@ -266,10 +243,12 @@ const VerticalCollection = Component.extend({
|
|
|
266
243
|
estimateHeight,
|
|
267
244
|
initialRenderCount,
|
|
268
245
|
items,
|
|
246
|
+
key,
|
|
269
247
|
renderAll,
|
|
270
248
|
renderFromLast,
|
|
271
249
|
shouldRecycle,
|
|
272
|
-
startingIndex
|
|
250
|
+
startingIndex,
|
|
251
|
+
occlusionTagName
|
|
273
252
|
}
|
|
274
253
|
);
|
|
275
254
|
|
|
@@ -308,12 +287,6 @@ VerticalCollection.reopenClass({
|
|
|
308
287
|
positionalParams: ['items']
|
|
309
288
|
});
|
|
310
289
|
|
|
311
|
-
if (!SUPPORTS_INVERSE_BLOCK) {
|
|
312
|
-
VerticalCollection.reopen({
|
|
313
|
-
shouldYieldToInverse: false
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
290
|
function calculateStartingIndex(items, idForFirstItem, key, renderFromLast) {
|
|
318
291
|
const totalItems = get(items, 'length');
|
|
319
292
|
|
|
@@ -334,30 +307,4 @@ function calculateStartingIndex(items, idForFirstItem, key, renderFromLast) {
|
|
|
334
307
|
return startingIndex;
|
|
335
308
|
}
|
|
336
309
|
|
|
337
|
-
function isPrepend(lenDiff, newItems, key, oldFirstKey, oldLastKey) {
|
|
338
|
-
const newItemsLength = get(newItems, 'length');
|
|
339
|
-
|
|
340
|
-
if (lenDiff <= 0 || lenDiff >= newItemsLength || newItemsLength === 0) {
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const newFirstKey = keyForItem(objectAt(newItems, lenDiff), key, lenDiff);
|
|
345
|
-
const newLastKey = keyForItem(objectAt(newItems, newItemsLength - 1), key, newItemsLength - 1);
|
|
346
|
-
|
|
347
|
-
return oldFirstKey === newFirstKey && oldLastKey === newLastKey;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function isAppend(lenDiff, newItems, key, oldFirstKey, oldLastKey) {
|
|
351
|
-
const newItemsLength = get(newItems, 'length');
|
|
352
|
-
|
|
353
|
-
if (lenDiff <= 0 || lenDiff >= newItemsLength || newItemsLength === 0) {
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const newFirstKey = keyForItem(objectAt(newItems, 0), key, 0);
|
|
358
|
-
const newLastKey = keyForItem(objectAt(newItems, newItemsLength - lenDiff - 1), key, newItemsLength - lenDiff - 1);
|
|
359
|
-
|
|
360
|
-
return oldFirstKey === newFirstKey && oldLastKey === newLastKey;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
310
|
export default VerticalCollection;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{{#each virtualComponents key="id" as |virtualComponent
|
|
1
|
+
{{#each virtualComponents key="id" as |virtualComponent| ~}}
|
|
2
2
|
{{~unbound virtualComponent.upperBound~}}
|
|
3
|
-
{{~#if virtualComponent.
|
|
3
|
+
{{~#if virtualComponent.isOccludedContent ~}}
|
|
4
4
|
{{{unbound virtualComponent.element}}}
|
|
5
5
|
{{~else~}}
|
|
6
6
|
{{~yield virtualComponent.content virtualComponent.index ~}}
|
package/addon/styles/app.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
occluded-content {
|
|
1
|
+
.occluded-content {
|
|
2
2
|
display: block;
|
|
3
3
|
position: relative;
|
|
4
4
|
width: 100%;
|
|
@@ -10,16 +10,17 @@ occluded-content {
|
|
|
10
10
|
color: rgba(0,0,0,0);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
table
|
|
14
|
-
tbody
|
|
15
|
-
thead
|
|
13
|
+
table .occluded-content,
|
|
14
|
+
tbody .occluded-content,
|
|
15
|
+
thead .occluded-content,
|
|
16
|
+
tfoot .occluded-content {
|
|
16
17
|
display: table-row;
|
|
17
18
|
position: relative;
|
|
18
19
|
width: 100%;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
ul
|
|
22
|
-
ol
|
|
22
|
+
ul .occluded-content,
|
|
23
|
+
ol .occluded-content {
|
|
23
24
|
display: list-item;
|
|
24
25
|
position: relative;
|
|
25
26
|
width: 100%;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from 'vertical-collection/components/vertical-collection/component';
|
|
1
|
+
export { default } from '@html-next/vertical-collection/components/vertical-collection/component';
|
package/config/environment.js
CHANGED