@html-next/vertical-collection 3.1.0 → 4.0.0-beta.2

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.
@@ -7,7 +7,7 @@ jobs:
7
7
  test:
8
8
  name: Run Tests
9
9
  runs-on: ubuntu-latest
10
- timeout-minutes: 8
10
+ timeout-minutes: 12
11
11
 
12
12
  steps:
13
13
  - name: Checkout
@@ -30,31 +30,31 @@ jobs:
30
30
  - name: Lint
31
31
  run: yarn lint
32
32
 
33
+ - name: Run Build
34
+ run: . bin/restore-env.sh && yarn run ember build
35
+
33
36
  - name: Run Tests
34
- run: bin/run-tests-with-retry.sh
35
- env:
36
- CI: true
37
+ uses: nick-fields/retry@v2
38
+ with:
39
+ timeout_minutes: 2
40
+ max_attempts: 5
41
+ command: . bin/restore-env.sh && CI=true yarn run ember test --path=dist
37
42
 
38
43
  test-ember-try:
39
44
  name: Run Tests
40
45
  runs-on: ubuntu-latest
41
- timeout-minutes: 15
46
+ timeout-minutes: 12
42
47
 
43
48
  strategy:
44
49
  fail-fast: false
45
50
  matrix:
46
51
  ember-version:
47
52
  [
48
- ember-lts-2.18,
49
- ember-lts-3.4,
50
- ember-lts-3.8,
51
53
  ember-lts-3.12,
52
54
  ember-lts-3.16,
53
55
  ember-lts-3.20,
54
- ember-lts-3.24,
55
56
  ember-lts-3.28,
56
57
  ember-lts-4.4,
57
- ember-4.5,
58
58
  ember-release,
59
59
  ember-beta,
60
60
  ember-canary,
@@ -91,5 +91,12 @@ jobs:
91
91
  if: steps.cache-npm.outputs.cache-hit != 'true'
92
92
  run: node_modules/.bin/ember try:one ${{ matrix.ember-version }} --skip-cleanup --- bin/stash-env.sh
93
93
 
94
+ - name: Run Build
95
+ run: . bin/restore-env.sh && yarn run ember build
96
+
94
97
  - name: Run Tests
95
- run: . bin/restore-env.sh && bin/run-tests-with-retry.sh
98
+ uses: nick-fields/retry@v2
99
+ with:
100
+ timeout_minutes: 2
101
+ max_attempts: 5
102
+ command: . bin/restore-env.sh && CI=true yarn run ember test --path=dist
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@ Changelog
2
2
  =========
3
3
 
4
4
 
5
+ ## v4.0.0-beta.2 (2022-09-08)
6
+
7
+
8
+ ## v4.0.0-beta.1 (2022-09-07)
9
+
10
+
11
+ ## v4.0.0-beta.0 (2022-08-28)
12
+
13
+ * Drop support for Ember versions prior to 3.12
14
+ * Drop support for Ember CLI 2.x
15
+ * Adopt native getters
16
+ * Adopt angle bracket invocation
17
+ * Drop positional param argument for `item`
18
+
19
+
5
20
  ## v3.1.0 (2022-08-04)
6
21
 
7
22
  #### :rocket: Enhancement
package/README.md CHANGED
@@ -30,24 +30,24 @@ ember install @html-next/vertical-collection
30
30
  ## Usage
31
31
 
32
32
  ```htmlbars
33
- {{#vertical-collection
34
- items
35
- tagName='ul'
36
- estimateHeight=50
37
- staticHeight=false
38
- bufferSize=1
39
- renderAll=false
40
- renderFromLast=false
41
- idForFirstItem=idForFirstItem
42
- firstReached=(action firstReached)
43
- lastReached=(action lastReached)
44
- firstVisibleChanged=(action firstVisibleChanged)
45
- lastVisibleChanged=(action lastVisibleChanged)
46
- as |item i|}}
33
+ <VerticalCollection
34
+ @items={{items}}
35
+ @tagName="ul"
36
+ @estimateHeight={{50}}
37
+ @staticHeight={{false}}
38
+ @bufferSize={{1}}
39
+ @renderAll={{false}}
40
+ @renderFromLast={{false}}
41
+ @idForFirstItem={{idForFirstItem}}
42
+ @firstReached={{firstReachedCallback}}
43
+ @lastReached={{lastReachedCallback}}
44
+ @firstVisibleChanged={{firstVisibleChangedCallback}}
45
+ @lastVisibleChanged={{lastVisibleChangedCallback}}
46
+ as |item i|>
47
47
  <li>
48
48
  {{item.number}} {{i}}
49
49
  </li>
50
- {{/vertical-collection}}
50
+ </VerticalCollection>
51
51
  ```
52
52
 
53
53
  ### Actions
@@ -67,6 +67,7 @@ ember install @html-next/vertical-collection
67
67
  | `^v1.x.x` | `v1.12.0 - v3.8.x` | `?` |
68
68
  | `^v2.x.x` | `v2.8.0 - v3.26.x` | `v12 - ?` |
69
69
  | `^v3.x.x` | `v2.18.0+` | `v14+` |
70
+ | `^v4.x.x` | `v3.12.0+` | `v14+` |
70
71
 
71
72
  ## Support, Questions, Collaboration
72
73
 
package/RELEASE.md CHANGED
@@ -58,3 +58,17 @@ release process. It will prompt you to to choose the version number after which
58
58
  you will have the chance to hand tweak the changelog to be used (for the
59
59
  `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging,
60
60
  pushing the tag and commits, etc.
61
+
62
+ To start a prerelease branch for a new major use:
63
+
64
+ ```sh
65
+ npx release-it major --preRelease=beta
66
+ ```
67
+
68
+ On subsequent prerelease run:
69
+
70
+ ```sh
71
+ npx release-it --preRelease
72
+ ```
73
+
74
+ For more guidance see https://github.com/release-it/release-it/blob/master/docs/pre-releases.md
@@ -1,6 +1,5 @@
1
1
  import { set } from '@ember/object';
2
2
  import { DEBUG } from '@glimmer/env';
3
- import { IS_GLIMMER_2, gte as emberVersionGTE } from 'ember-compatibility-helpers';
4
3
 
5
4
  import document from '../../utils/document-shim';
6
5
 
@@ -27,12 +26,6 @@ export default class OccludedContent {
27
26
  this.isOccludedContent = true;
28
27
  this.rendered = false;
29
28
 
30
- if (!emberVersionGTE('3.0.0')) {
31
- // In older versions of Ember, binding anything on an object in the template
32
- // adds observers which creates __ember_meta__
33
- this.__ember_meta__ = null; // eslint-disable-line camelcase
34
- }
35
-
36
29
  if (DEBUG) {
37
30
  Object.preventExtensions(this);
38
31
  }
@@ -57,11 +50,11 @@ export default class OccludedContent {
57
50
  }
58
51
 
59
52
  get realUpperBound() {
60
- return IS_GLIMMER_2 ? this.upperBound : this.upperBound.previousSibling;
53
+ return this.upperBound;
61
54
  }
62
55
 
63
56
  get realLowerBound() {
64
- return IS_GLIMMER_2 ? this.lowerBound : this.lowerBound.nextSibling;
57
+ return this.lowerBound;
65
58
  }
66
59
 
67
60
  get parentNode() {
@@ -1,7 +1,6 @@
1
1
  import { set } from '@ember/object';
2
2
  import { assert } from '@ember/debug';
3
3
  import { DEBUG } from '@glimmer/env';
4
- import { IS_GLIMMER_2, gte as emberVersionGTE } from 'ember-compatibility-helpers';
5
4
 
6
5
  import document from '../../utils/document-shim';
7
6
 
@@ -22,23 +21,17 @@ export default class VirtualComponent {
22
21
 
23
22
  this.rendered = false;
24
23
 
25
- if (!emberVersionGTE('3.0.0')) {
26
- // In older versions of Ember, binding anything on an object in the template
27
- // adds observers which creates __ember_meta__
28
- this.__ember_meta__ = null; // eslint-disable-line camelcase
29
- }
30
-
31
24
  if (DEBUG) {
32
25
  Object.preventExtensions(this);
33
26
  }
34
27
  }
35
28
 
36
29
  get realUpperBound() {
37
- return IS_GLIMMER_2 ? this.upperBound : this.upperBound.previousSibling;
30
+ return this.upperBound;
38
31
  }
39
32
 
40
33
  get realLowerBound() {
41
- return IS_GLIMMER_2 ? this.lowerBound : this.lowerBound.nextSibling;
34
+ return this.lowerBound;
42
35
  }
43
36
 
44
37
  getBoundingClientRect() {
@@ -539,6 +539,9 @@ export default class Radar {
539
539
  if (item) {
540
540
  insertRangeBefore(this._domPool, null, component.realUpperBound, component.realLowerBound);
541
541
  } else {
542
+ // Insert the virtual component bound back to make sure Glimmer is
543
+ // not confused about the state of the DOM.
544
+ insertRangeBefore(this._itemContainer, null, component.realUpperBound, component.realLowerBound);
542
545
  run(() => {
543
546
  virtualComponents.removeObject(component);
544
547
  });
@@ -579,7 +582,7 @@ export default class Radar {
579
582
  if (component.rendered === true) {
580
583
  insertRangeBefore(_itemContainer, relativeNode, component.realUpperBound, component.realLowerBound);
581
584
  } else {
582
- virtualComponents.insertAt(virtualComponents.get('length') - 1, component);
585
+ virtualComponents.insertAt(virtualComponents.length - 1, component);
583
586
  component.rendered = true;
584
587
 
585
588
  // shouldRecycle=false breaks UI when scrolling the elements fast.
@@ -620,7 +623,7 @@ export default class Radar {
620
623
  if (component.rendered === true) {
621
624
  insertRangeBefore(_itemContainer, relativeNode, component.realUpperBound, component.realLowerBound);
622
625
  } else {
623
- virtualComponents.insertAt(virtualComponents.get('length') - 1, component);
626
+ virtualComponents.insertAt(virtualComponents.length - 1, component);
624
627
  component.rendered = true;
625
628
 
626
629
  // Components that are both new and prepended still need to be rendered at the end because Glimmer.
@@ -1,9 +1,13 @@
1
+ import { DEBUG } from '@glimmer/env';
2
+ import { assert } from '@ember/debug';
3
+
1
4
  import { empty, readOnly } from '@ember/object/computed';
2
5
 
3
6
  import Component from '@ember/component';
4
7
  import { get, computed } from '@ember/object';
5
8
  import { run } from '@ember/runloop';
6
9
  import layout from './template';
10
+ import { ViewportContainer } from '../../-private';
7
11
 
8
12
  import { scheduler, Token } from 'ember-raf-scheduler';
9
13
 
@@ -14,6 +18,166 @@ import {
14
18
  objectAt
15
19
  } from '../../-private';
16
20
 
21
+ /*
22
+ * BEGIN DEBUG HELPERS
23
+ *
24
+ * These methods and the Visualization class are used by DEBUG code paths.
25
+ */
26
+ function isNonZero(value) {
27
+ let int = parseInt(value, 10);
28
+ let float = parseFloat(value);
29
+
30
+ return !isNaN(int) && (int !== 0 || float !== 0);
31
+ }
32
+
33
+ function hasStyleValue(styles, key, value) {
34
+ return styles[key] === value;
35
+ }
36
+
37
+ function hasStyleWithNonZeroValue(styles, key) {
38
+ return isNonZero(styles[key]);
39
+ }
40
+
41
+ function styleIsOneOf(styles, key, values) {
42
+ return styles[key] && values.indexOf(styles[key]) !== -1;
43
+ }
44
+
45
+ function applyVerticalStyles(element, geography) {
46
+ element.style.height = `${geography.height}px`;
47
+ element.style.top = `${geography.top}px`;
48
+ }
49
+
50
+ class Visualization {
51
+ constructor(radar) {
52
+ this.radar = radar;
53
+ this.satellites = [];
54
+ this.cache = [];
55
+
56
+ this.wrapper = document.createElement('div');
57
+ this.wrapper.className = 'vertical-collection-visual-debugger';
58
+
59
+ this.container = document.createElement('div');
60
+ this.container.className = 'vc_visualization-container';
61
+ this.wrapper.appendChild(this.container);
62
+
63
+ this.itemContainer = document.createElement('div');
64
+ this.itemContainer.className = 'vc_visualization-item-container';
65
+ this.container.appendChild(this.itemContainer);
66
+
67
+ this.scrollContainer = document.createElement('div');
68
+ this.scrollContainer.className = 'vc_visualization-scroll-container';
69
+ this.container.appendChild(this.scrollContainer);
70
+
71
+ this.screen = document.createElement('div');
72
+ this.screen.className = 'vc_visualization-screen';
73
+ this.container.appendChild(this.screen);
74
+
75
+ document.body.appendChild(this.wrapper);
76
+ }
77
+
78
+ render() {
79
+ this.styleViewport();
80
+ this.updateSatellites();
81
+ }
82
+
83
+ styleViewport() {
84
+ const { _scrollContainer } = this.radar;
85
+ this.container.style.height = `${_scrollContainer.getBoundingClientRect().height}px`;
86
+
87
+ applyVerticalStyles(this.scrollContainer, _scrollContainer.getBoundingClientRect());
88
+ applyVerticalStyles(this.screen, ViewportContainer.getBoundingClientRect());
89
+ }
90
+
91
+ makeSatellite() {
92
+ let satellite;
93
+
94
+ if (this.cache.length) {
95
+ satellite = this.cache.pop();
96
+ } else {
97
+ satellite = document.createElement('div');
98
+ satellite.className = 'vc_visualization-virtual-component';
99
+ }
100
+
101
+ this.satellites.push(satellite);
102
+ this.itemContainer.append(satellite);
103
+ }
104
+
105
+ updateSatellites() {
106
+ const { satellites: sats } = this;
107
+ let {
108
+ firstItemIndex,
109
+ lastItemIndex,
110
+
111
+ totalItems,
112
+
113
+ totalBefore,
114
+ totalAfter,
115
+ skipList,
116
+ _calculatedEstimateHeight
117
+ } = this.radar;
118
+
119
+ const isDynamic = !!skipList;
120
+ const itemHeights = isDynamic && skipList.values;
121
+
122
+ const firstVisualizedIndex = Math.max(firstItemIndex - 10, 0);
123
+ const lastVisualizedIndex = Math.min(lastItemIndex + 10, totalItems - 1);
124
+
125
+ const lengthWithBuffer = lastVisualizedIndex - firstVisualizedIndex + 1;
126
+ const isShrinking = sats.length > lengthWithBuffer;
127
+
128
+ while (sats.length !== lengthWithBuffer) {
129
+ if (isShrinking) {
130
+ const satellite = sats.pop();
131
+
132
+ satellite.parentNode.removeChild(satellite);
133
+ this.cache.push(satellite);
134
+ } else {
135
+ this.makeSatellite();
136
+ }
137
+ }
138
+
139
+ for (let itemIndex = firstVisualizedIndex, i = 0; itemIndex <= lastVisualizedIndex; itemIndex++, i++) {
140
+ const element = sats[i];
141
+
142
+ const itemHeight = isDynamic ? itemHeights[itemIndex] : _calculatedEstimateHeight;
143
+
144
+ element.style.height = `${itemHeight}px`;
145
+ element.setAttribute('index', String(itemIndex));
146
+ element.innerText = String(itemIndex);
147
+
148
+ if (itemIndex < firstItemIndex) {
149
+ element.classList.add('culled');
150
+ totalBefore -= itemHeight;
151
+ } else if (itemIndex > lastItemIndex) {
152
+ element.classList.add('culled');
153
+ totalAfter -= itemHeight;
154
+ } else {
155
+ element.classList.remove('culled');
156
+ }
157
+ }
158
+
159
+ this.itemContainer.style.paddingTop = `${totalBefore}px`;
160
+ this.itemContainer.style.paddingBottom = `${totalAfter}px`;
161
+ }
162
+
163
+ destroy() {
164
+ this.wrapper.parentNode.removeChild(this.wrapper);
165
+ this.wrapper = null;
166
+ this.radar = null;
167
+ this.component = null;
168
+ this.satellites.forEach((satellite) => {
169
+ if (satellite.parentNode) {
170
+ satellite.parentNode.removeChild(satellite);
171
+ }
172
+ });
173
+ this.satellites = null;
174
+ this.cache = null;
175
+ }
176
+ }
177
+ /*
178
+ * END DEBUG HELPERS
179
+ */
180
+
17
181
  const VerticalCollection = Component.extend({
18
182
  layout,
19
183
 
@@ -43,7 +207,7 @@ const VerticalCollection = Component.extend({
43
207
 
44
208
  /**
45
209
  * List of objects to svelte-render.
46
- * Can be called like `{{#vertical-collection <items-array>}}`, since it's the first positional parameter of this component.
210
+ * Can be called like `<VerticalCollection @items={{itemsArray}} />`.
47
211
  *
48
212
  * @property items
49
213
  * @type Array
@@ -155,12 +319,12 @@ const VerticalCollection = Component.extend({
155
319
  virtualComponents: computed('items.[]', 'renderAll', 'estimateHeight', 'bufferSize', function() {
156
320
  const { _radar } = this;
157
321
 
158
- const items = this.get('items');
322
+ const items = this.items;
159
323
 
160
324
  _radar.items = items === null || items === undefined ? [] : items;
161
- _radar.estimateHeight = this.get('estimateHeight');
162
- _radar.renderAll = this.get('renderAll');
163
- _radar.bufferSize = this.get('bufferSize');
325
+ _radar.estimateHeight = this.estimateHeight;
326
+ _radar.renderAll = this.renderAll;
327
+ _radar.bufferSize = this.bufferSize;
164
328
 
165
329
  _radar.scheduleUpdate(true);
166
330
 
@@ -179,8 +343,8 @@ const VerticalCollection = Component.extend({
179
343
  this._nextSendActions = null;
180
344
 
181
345
  run(() => {
182
- const items = this.get('items');
183
- const keyPath = this.get('key');
346
+ const items = this.items;
347
+ const keyPath = this.key;
184
348
 
185
349
  this._scheduledActions.forEach(([action, index]) => {
186
350
  const item = objectAt(items, index);
@@ -200,7 +364,7 @@ const VerticalCollection = Component.extend({
200
364
  }
201
365
  },
202
366
 
203
- /* Public API Methods
367
+ /* Public API Methods
204
368
  @index => number
205
369
  This will return offset height of the indexed item.
206
370
  */
@@ -228,11 +392,19 @@ const VerticalCollection = Component.extend({
228
392
  willDestroy() {
229
393
  this.token.cancel();
230
394
  this._radar.destroy();
231
- let registerAPI = this.get('registerAPI');
395
+ let registerAPI = this.registerAPI;
232
396
  if (registerAPI) {
233
397
  registerAPI(null);
234
398
  }
235
399
  clearTimeout(this._nextSendActions);
400
+
401
+ if (DEBUG) {
402
+ if (this.__visualization) {
403
+ console.info('destroying visualization'); // eslint-disable-line no-console
404
+ this.__visualization.destroy();
405
+ this.__visualization = null;
406
+ }
407
+ }
236
408
  },
237
409
 
238
410
  init() {
@@ -241,19 +413,20 @@ const VerticalCollection = Component.extend({
241
413
  this.token = new Token();
242
414
  const RadarClass = this.staticHeight ? StaticRadar : DynamicRadar;
243
415
 
244
- const items = this.get('items') || [];
245
-
246
- const bufferSize = this.get('bufferSize');
247
- const containerSelector = this.get('containerSelector');
248
- const estimateHeight = this.get('estimateHeight');
249
- const initialRenderCount = this.get('initialRenderCount');
250
- const renderAll = this.get('renderAll');
251
- const renderFromLast = this.get('renderFromLast');
252
- const shouldRecycle = this.get('shouldRecycle');
253
- const occlusionTagName = this.get('occlusionTagName');
254
-
255
- const idForFirstItem = this.get('idForFirstItem');
256
- const key = this.get('key');
416
+ const items = this.items || [];
417
+
418
+ const {
419
+ bufferSize,
420
+ containerSelector,
421
+ estimateHeight,
422
+ initialRenderCount,
423
+ renderAll,
424
+ renderFromLast,
425
+ shouldRecycle,
426
+ occlusionTagName,
427
+ idForFirstItem,
428
+ key
429
+ } = this;
257
430
 
258
431
  const startingIndex = calculateStartingIndex(items, idForFirstItem, key, renderFromLast);
259
432
 
@@ -303,24 +476,24 @@ const VerticalCollection = Component.extend({
303
476
  };
304
477
  }
305
478
 
306
- /* Public methods to Expose to parent
479
+ /* Public methods to Expose to parent
307
480
 
308
481
  Usage:
309
482
 
310
483
  Template:
311
484
 
312
- {{vertical-collection registerAPI=(action "registerAPI")}}
485
+ <VerticalCollection @registerAPI={{action "registerAPI"}} />
313
486
 
314
487
  Component:
315
488
 
316
- export default Component.extend({
489
+ export default Component.extend({
317
490
  actions: {
318
491
  registerAPI(api) {
319
492
  this.set('collectionAPI', api);
320
493
  }
321
494
  },
322
495
  scrollToItem() {
323
- let collectionAPI = this.get('collectionAPI');
496
+ let collectionAPI = this.collectionAPI;
324
497
  collectionAPI.scrollToItem(index);
325
498
  }
326
499
  });
@@ -339,11 +512,77 @@ const VerticalCollection = Component.extend({
339
512
  };
340
513
  registerAPI(publicAPI);
341
514
  }
342
- }
343
- });
344
515
 
345
- VerticalCollection.reopenClass({
346
- positionalParams: ['items']
516
+ if (DEBUG) {
517
+ this.__visualization = null;
518
+ this._radar._debugDidUpdate = () => {
519
+
520
+ // Update visualization
521
+ //
522
+ // This debugging mode can be controlled via the argument
523
+ // `@debugVis={{true}}` at component invocation.
524
+ //
525
+ if (this.debugVis !== true) {
526
+ if (this.__visualization !== null) {
527
+ console.info('tearing down existing visualization'); // eslint-disable-line no-console
528
+ this.__visualization.destroy();
529
+ this.__visualization = null;
530
+ }
531
+ return;
532
+ }
533
+
534
+ if (this.__visualization === null) {
535
+ this.__visualization = new Visualization(this._radar);
536
+ }
537
+
538
+ this.__visualization.render();
539
+
540
+ // Detect issues with CSS
541
+ //
542
+ // This debugging mode can be controlled via the argument
543
+ // `@debugCSS={{true}}` at component invocation.
544
+ //
545
+ if (this.debugCSS !== true) {
546
+ return;
547
+ }
548
+
549
+ let radar = this._radar;
550
+ let styles;
551
+
552
+ // check telescope
553
+ if (radar.scrollContainer !== ViewportContainer) {
554
+ styles = window.getComputedStyle(radar.scrollContainer);
555
+ } else {
556
+ styles = window.getComputedStyle(document.body);
557
+ }
558
+
559
+ assert(`scrollContainer cannot be inline.`, styleIsOneOf(styles, 'display', ['block', 'inline-block', 'flex', 'inline-flex']));
560
+ assert(`scrollContainer must define position`, styleIsOneOf(styles, 'position', ['static', 'relative', 'absolute']));
561
+ assert(`scrollContainer must define height or max-height`, hasStyleWithNonZeroValue(styles, 'height') || hasStyleWithNonZeroValue(styles, 'max-height'));
562
+
563
+ // conditional perf check for non-body scrolling
564
+ if (radar.scrollContainer !== ViewportContainer) {
565
+ assert(`scrollContainer must define overflow-y`, hasStyleValue(styles, 'overflow-y', 'scroll') || hasStyleValue(styles, 'overflow', 'scroll'));
566
+ }
567
+
568
+ // check itemContainer
569
+ styles = window.getComputedStyle(radar.itemContainer);
570
+
571
+ assert(`itemContainer cannot be inline.`, styleIsOneOf(styles, 'display', ['block', 'inline-block', 'flex', 'inline-flex']));
572
+ assert(`itemContainer must define position`, styleIsOneOf(styles, 'position', ['static', 'relative', 'absolute']));
573
+
574
+ // check item defaults
575
+ assert(`You must supply at least one item to the collection to debug it's CSS.`, this.items.length);
576
+
577
+ let element = radar._itemContainer.firstElementChild;
578
+
579
+ styles = window.getComputedStyle(element);
580
+
581
+ assert(`Item cannot be inline.`, styleIsOneOf(styles, 'display', ['block', 'inline-block', 'flex', 'inline-flex']));
582
+ assert(`Item must define position`, styleIsOneOf(styles, 'position', ['static', 'relative', 'absolute']));
583
+ };
584
+ }
585
+ }
347
586
  });
348
587
 
349
588
  function calculateStartingIndex(items, idForFirstItem, key, renderFromLast) {
@@ -366,4 +605,4 @@ function calculateStartingIndex(items, idForFirstItem, key, renderFromLast) {
366
605
  return startingIndex;
367
606
  }
368
607
 
369
- export default VerticalCollection;
608
+ export default VerticalCollection;
@@ -18,10 +18,7 @@ function retry {
18
18
  sleep 1
19
19
  fi
20
20
  done
21
- if [[ $retval -ne 0 ]] && [[ $attempt -gt 4 ]]; then
22
- # Something is fubar, go ahead and exit
23
- exit $retval
24
- fi
21
+ exit $retval
25
22
  }
26
23
 
27
24
  yarn run ember build && retry yarn run ember test --path=dist
package/index.js CHANGED
@@ -6,13 +6,6 @@ const Rollup = require('broccoli-rollup');
6
6
  const merge = require('broccoli-merge-trees');
7
7
  const VersionChecker = require('ember-cli-version-checker');
8
8
 
9
- function isProductionEnv() {
10
- const isProd = /production/.test(process.env.EMBER_ENV);
11
- const isTest = process.env.EMBER_CLI_TEST_COMMAND;
12
-
13
- return isProd && !isTest;
14
- }
15
-
16
9
  module.exports = {
17
10
  name: require('./package').name,
18
11
 
@@ -23,13 +16,7 @@ module.exports = {
23
16
  },
24
17
 
25
18
  getOutputDirForVersion() {
26
- let VersionChecker = require('ember-cli-version-checker');
27
- let checker = new VersionChecker(this);
28
- let emberCli = checker.for('ember-cli', 'npm');
29
-
30
- let requiresModulesDir = emberCli.satisfies('< 3.0.0');
31
-
32
- return requiresModulesDir ? 'modules' : '';
19
+ return '';
33
20
  },
34
21
 
35
22
  // Borrowed from ember-cli-babel
@@ -49,10 +36,8 @@ module.exports = {
49
36
  let withoutPrivate = new Funnel(tree, {
50
37
  exclude: [
51
38
  '**/**.hbs',
52
- '-private',
53
- isProductionEnv() ? '-debug' : false
54
- ].filter(Boolean),
55
-
39
+ '-private'
40
+ ],
56
41
  destDir: '@html-next/vertical-collection'
57
42
  });
58
43
 
@@ -170,34 +155,7 @@ module.exports = {
170
155
  this._setupBabelOptions(app.env);
171
156
 
172
157
  if (!/production/.test(app.env) && !/test/.test(app.env)) {
173
- findImporter(this).import('vendor/debug.css');
158
+ this.import('vendor/debug.css');
174
159
  }
175
- },
176
-
177
- treeForApp() {
178
- const tree = this._super.treeForApp.apply(this, arguments);
179
-
180
- const exclude = [];
181
-
182
- if (isProductionEnv()) {
183
- exclude.push('initializers/debug.js');
184
- }
185
-
186
- return new Funnel(tree, { exclude });
187
160
  }
188
161
  };
189
-
190
- function findImporter(addon) {
191
- if (typeof addon.import === 'function') {
192
- // If addon.import() is present (CLI 2.7+) use that
193
- return addon;
194
- } else {
195
- // Otherwise, reuse the _findHost implementation that would power addon.import()
196
- let current = addon;
197
- let app;
198
- do {
199
- app = current.app || app;
200
- } while (current.parent.parent && (current = current.parent));
201
- return app;
202
- }
203
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@html-next/vertical-collection",
3
- "version": "3.1.0",
3
+ "version": "4.0.0-beta.2",
4
4
  "description": "infinite-scroll, done right. done.",
5
5
  "keywords": [
6
6
  "occlusion",
@@ -52,22 +52,21 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "babel6-plugin-strip-class-callcheck": "^6.0.0",
55
- "broccoli-funnel": "^2.0.2",
56
- "broccoli-merge-trees": "^3.0.1",
57
- "broccoli-rollup": "^4.1.1",
55
+ "broccoli-funnel": "^3.0.8",
56
+ "broccoli-merge-trees": "^4.2.0",
57
+ "broccoli-rollup": "^5.0.0",
58
58
  "ember-cli-babel": "^7.12.0",
59
- "ember-cli-htmlbars": "^5.0.0",
60
- "ember-cli-version-checker": "^3.1.3",
61
- "ember-compatibility-helpers": "^1.2.1",
59
+ "ember-cli-htmlbars": "^6.0.0",
60
+ "ember-cli-version-checker": "^5.1.2",
62
61
  "ember-raf-scheduler": "^0.3.0"
63
62
  },
64
63
  "devDependencies": {
65
64
  "@ember/optional-features": "^2.0.0",
66
- "@ember/test-helpers": "^2.4.0",
65
+ "@ember/test-helpers": "^2.8.1",
67
66
  "babel-eslint": "^10.1.0",
68
67
  "bootstrap": "~3.3.5",
69
68
  "broccoli-asset-rev": "^3.0.0",
70
- "ember-auto-import": "^2.0.0",
69
+ "ember-auto-import": "^2.4.2",
71
70
  "ember-cli": "~3.28.0",
72
71
  "ember-cli-dependency-checker": "^3.2.0",
73
72
  "ember-cli-eslint": "^5.1.0",
@@ -82,17 +81,17 @@
82
81
  "ember-export-application-global": "^2.0.1",
83
82
  "ember-load-initializers": "^2.1.2",
84
83
  "ember-perf-timeline": "^2.0.0",
85
- "ember-qunit": "^5.0.0",
84
+ "ember-qunit": "^5.1.5",
86
85
  "ember-resolver": "^8.0.2",
87
86
  "ember-source": "~3.12.0",
88
87
  "ember-source-channel-url": "^3.0.0",
89
- "ember-try": "^1.4.0",
88
+ "ember-try": "^2.0.0",
90
89
  "eslint": "^7.32.0",
91
90
  "eslint-plugin-ember": "^6.7.1",
92
91
  "eslint-plugin-node": "^9.1.0",
93
92
  "loader.js": "^4.7.0",
94
93
  "npm-run-all": "^4.1.5",
95
- "qunit": "^2.0.0",
94
+ "qunit": "^2.19.1",
96
95
  "qunit-dom": "^1.0.0",
97
96
  "release-it": "^14.2.1",
98
97
  "release-it-lerna-changelog": "^3.1.0",
@@ -1,93 +0,0 @@
1
- import { assert } from '@ember/debug';
2
- import Mixin from '@ember/object/mixin';
3
- import Visualization from './visualization';
4
- import { ViewportContainer } from '../../-private';
5
-
6
- import {
7
- styleIsOneOf,
8
- hasStyleValue,
9
- hasStyleWithNonZeroValue
10
- } from '../utils/validate-style';
11
-
12
- export default Mixin.create({
13
- debugVis: false,
14
- debugCSS: false,
15
-
16
- __visualization: null,
17
-
18
- init() {
19
- this._super(...arguments);
20
-
21
- this._radar._debugDidUpdate = () => {
22
- this.updateVisualization();
23
- this.detectIssuesWithCSS();
24
- };
25
- },
26
-
27
- detectIssuesWithCSS() {
28
- if (this.get('debugCSS') === false) {
29
- return;
30
- }
31
-
32
- let radar = this._radar;
33
- let styles;
34
-
35
- // check telescope
36
- if (radar.scrollContainer !== ViewportContainer) {
37
- styles = window.getComputedStyle(radar.scrollContainer);
38
- } else {
39
- styles = window.getComputedStyle(document.body);
40
- }
41
-
42
- assert(`scrollContainer cannot be inline.`, styleIsOneOf(styles, 'display', ['block', 'inline-block', 'flex', 'inline-flex']));
43
- assert(`scrollContainer must define position`, styleIsOneOf(styles, 'position', ['static', 'relative', 'absolute']));
44
- assert(`scrollContainer must define height or max-height`, hasStyleWithNonZeroValue(styles, 'height') || hasStyleWithNonZeroValue(styles, 'max-height'));
45
-
46
- // conditional perf check for non-body scrolling
47
- if (radar.scrollContainer !== ViewportContainer) {
48
- assert(`scrollContainer must define overflow-y`, hasStyleValue(styles, 'overflow-y', 'scroll') || hasStyleValue(styles, 'overflow', 'scroll'));
49
- }
50
-
51
- // check itemContainer
52
- styles = window.getComputedStyle(radar.itemContainer);
53
-
54
- assert(`itemContainer cannot be inline.`, styleIsOneOf(styles, 'display', ['block', 'inline-block', 'flex', 'inline-flex']));
55
- assert(`itemContainer must define position`, styleIsOneOf(styles, 'position', ['static', 'relative', 'absolute']));
56
-
57
- // check item defaults
58
- assert(`You must supply at least one item to the collection to debug it's CSS.`, this.get('items.length'));
59
-
60
- let element = radar._itemContainer.firstElementChild;
61
-
62
- styles = window.getComputedStyle(element);
63
-
64
- assert(`Item cannot be inline.`, styleIsOneOf(styles, 'display', ['block', 'inline-block', 'flex', 'inline-flex']));
65
- assert(`Item must define position`, styleIsOneOf(styles, 'position', ['static', 'relative', 'absolute']));
66
- },
67
-
68
- updateVisualization() {
69
- if (this.get('debugVis') === false) {
70
- if (this.__visualization !== null) {
71
- console.info('tearing down existing visualization'); // eslint-disable-line no-console
72
- this.__visualization.destroy();
73
- this.__visualization = null;
74
- }
75
- return;
76
- }
77
-
78
- if (this.__visualization === null) {
79
- this.__visualization = new Visualization(this._radar);
80
- }
81
-
82
- this.__visualization.render();
83
- },
84
-
85
- willDestroy() {
86
- this._super();
87
- if (this.__visualization) {
88
- console.info('destroying visualization'); // eslint-disable-line no-console
89
- this.__visualization.destroy();
90
- this.__visualization = null;
91
- }
92
- }
93
- });
@@ -1,134 +0,0 @@
1
- import { ViewportContainer } from '../../-private';
2
-
3
- function applyVerticalStyles(element, geography) {
4
- element.style.height = `${geography.height}px`;
5
- element.style.top = `${geography.top}px`;
6
- }
7
-
8
- export default class Visualization {
9
- constructor(radar) {
10
- this.radar = radar;
11
- this.satellites = [];
12
- this.cache = [];
13
-
14
- this.wrapper = document.createElement('div');
15
- this.wrapper.className = 'vertical-collection-visual-debugger';
16
-
17
- this.container = document.createElement('div');
18
- this.container.className = 'vc_visualization-container';
19
- this.wrapper.appendChild(this.container);
20
-
21
- this.itemContainer = document.createElement('div');
22
- this.itemContainer.className = 'vc_visualization-item-container';
23
- this.container.appendChild(this.itemContainer);
24
-
25
- this.scrollContainer = document.createElement('div');
26
- this.scrollContainer.className = 'vc_visualization-scroll-container';
27
- this.container.appendChild(this.scrollContainer);
28
-
29
- this.screen = document.createElement('div');
30
- this.screen.className = 'vc_visualization-screen';
31
- this.container.appendChild(this.screen);
32
-
33
- document.body.appendChild(this.wrapper);
34
- }
35
-
36
- render() {
37
- this.styleViewport();
38
- this.updateSatellites();
39
- }
40
-
41
- styleViewport() {
42
- const { _scrollContainer } = this.radar;
43
- this.container.style.height = `${_scrollContainer.getBoundingClientRect().height}px`;
44
-
45
- applyVerticalStyles(this.scrollContainer, _scrollContainer.getBoundingClientRect());
46
- applyVerticalStyles(this.screen, ViewportContainer.getBoundingClientRect());
47
- }
48
-
49
- makeSatellite() {
50
- let satellite;
51
-
52
- if (this.cache.length) {
53
- satellite = this.cache.pop();
54
- } else {
55
- satellite = document.createElement('div');
56
- satellite.className = 'vc_visualization-virtual-component';
57
- }
58
-
59
- this.satellites.push(satellite);
60
- this.itemContainer.append(satellite);
61
- }
62
-
63
- updateSatellites() {
64
- const { satellites: sats } = this;
65
- let {
66
- firstItemIndex,
67
- lastItemIndex,
68
-
69
- totalItems,
70
-
71
- totalBefore,
72
- totalAfter,
73
- skipList,
74
- _calculatedEstimateHeight
75
- } = this.radar;
76
-
77
- const isDynamic = !!skipList;
78
- const itemHeights = isDynamic && skipList.values;
79
-
80
- const firstVisualizedIndex = Math.max(firstItemIndex - 10, 0);
81
- const lastVisualizedIndex = Math.min(lastItemIndex + 10, totalItems - 1);
82
-
83
- const lengthWithBuffer = lastVisualizedIndex - firstVisualizedIndex + 1;
84
- const isShrinking = sats.length > lengthWithBuffer;
85
-
86
- while (sats.length !== lengthWithBuffer) {
87
- if (isShrinking) {
88
- const satellite = sats.pop();
89
-
90
- satellite.parentNode.removeChild(satellite);
91
- this.cache.push(satellite);
92
- } else {
93
- this.makeSatellite();
94
- }
95
- }
96
-
97
- for (let itemIndex = firstVisualizedIndex, i = 0; itemIndex <= lastVisualizedIndex; itemIndex++, i++) {
98
- const element = sats[i];
99
-
100
- const itemHeight = isDynamic ? itemHeights[itemIndex] : _calculatedEstimateHeight;
101
-
102
- element.style.height = `${itemHeight}px`;
103
- element.setAttribute('index', String(itemIndex));
104
- element.innerText = String(itemIndex);
105
-
106
- if (itemIndex < firstItemIndex) {
107
- element.classList.add('culled');
108
- totalBefore -= itemHeight;
109
- } else if (itemIndex > lastItemIndex) {
110
- element.classList.add('culled');
111
- totalAfter -= itemHeight;
112
- } else {
113
- element.classList.remove('culled');
114
- }
115
- }
116
-
117
- this.itemContainer.style.paddingTop = `${totalBefore}px`;
118
- this.itemContainer.style.paddingBottom = `${totalAfter}px`;
119
- }
120
-
121
- destroy() {
122
- this.wrapper.parentNode.removeChild(this.wrapper);
123
- this.wrapper = null;
124
- this.radar = null;
125
- this.component = null;
126
- this.satellites.forEach((satellite) => {
127
- if (satellite.parentNode) {
128
- satellite.parentNode.removeChild(satellite);
129
- }
130
- });
131
- this.satellites = null;
132
- this.cache = null;
133
- }
134
- }
@@ -1,4 +0,0 @@
1
- import DebugMixin from './edge-visualization/debug-mixin';
2
- import Collection from '../components/vertical-collection/component';
3
-
4
- Collection.reopen(DebugMixin);
@@ -1,12 +0,0 @@
1
- export function hasCSSRule(rules, prop, value) {
2
- let styleStr = `${prop}:\\s*${value}`;
3
- let expr = new RegExp(styleStr, ['i']);
4
-
5
- for (let i = 0; i < rules.length; i++) {
6
- if (expr.test(rules[i].cssText)) {
7
- return true;
8
- }
9
- }
10
-
11
- return false;
12
- }
@@ -1,13 +0,0 @@
1
- import { isNonZero } from './validate-style';
2
-
3
- export function hasDimension(rect, prop) {
4
- return isNonZero(rect[prop]);
5
- }
6
-
7
- export function hasDimensionAbove(rect, prop, amount) {
8
- return hasDimension(rect, prop) && rect[prop] >= amount;
9
- }
10
-
11
- export function hasDimensionEqual(rect, prop, amount) {
12
- return hasDimension(rect, prop) && rect[prop] === amount;
13
- }
@@ -1,23 +0,0 @@
1
-
2
- export function hasStyleValue(styles, key, value) {
3
- return styles[key] === value;
4
- }
5
-
6
- export function isNonZero(value) {
7
- let int = parseInt(value, 10);
8
- let float = parseFloat(value);
9
-
10
- return !isNaN(int) && (int !== 0 || float !== 0);
11
- }
12
-
13
- export function hasStyleWithNonZeroValue(styles, key) {
14
- return isNonZero(styles[key]);
15
- }
16
-
17
- export function styleIsOneOf(styles, key, values) {
18
- return styles[key] && values.indexOf(styles[key]) !== -1;
19
- }
20
-
21
- export function containsStyleValue(styles, key, value) {
22
- return styles[key] && styles[key].indexOf(value) !== -1;
23
- }
@@ -1,6 +0,0 @@
1
- import '@html-next/vertical-collection/-debug';
2
-
3
- export default {
4
- name: 'vertical-collection-debug',
5
- initialize() {}
6
- };