@html-next/vertical-collection 4.0.0-beta.1 → 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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@ Changelog
2
2
  =========
3
3
 
4
4
 
5
+ ## v4.0.0-beta.2 (2022-09-08)
6
+
7
+
5
8
  ## v4.0.0-beta.1 (2022-09-07)
6
9
 
7
10
 
@@ -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
 
@@ -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
  */
@@ -233,6 +397,14 @@ const VerticalCollection = Component.extend({
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() {
@@ -304,7 +476,7 @@ const VerticalCollection = Component.extend({
304
476
  };
305
477
  }
306
478
 
307
- /* Public methods to Expose to parent
479
+ /* Public methods to Expose to parent
308
480
 
309
481
  Usage:
310
482
 
@@ -340,6 +512,76 @@ const VerticalCollection = Component.extend({
340
512
  };
341
513
  registerAPI(publicAPI);
342
514
  }
515
+
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
+ }
343
585
  }
344
586
  });
345
587
 
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
 
@@ -43,10 +36,8 @@ module.exports = {
43
36
  let withoutPrivate = new Funnel(tree, {
44
37
  exclude: [
45
38
  '**/**.hbs',
46
- '-private',
47
- isProductionEnv() ? '-debug' : false
48
- ].filter(Boolean),
49
-
39
+ '-private'
40
+ ],
50
41
  destDir: '@html-next/vertical-collection'
51
42
  });
52
43
 
@@ -164,34 +155,7 @@ module.exports = {
164
155
  this._setupBabelOptions(app.env);
165
156
 
166
157
  if (!/production/.test(app.env) && !/test/.test(app.env)) {
167
- findImporter(this).import('vendor/debug.css');
158
+ this.import('vendor/debug.css');
168
159
  }
169
- },
170
-
171
- treeForApp() {
172
- const tree = this._super.treeForApp.apply(this, arguments);
173
-
174
- const exclude = [];
175
-
176
- if (isProductionEnv()) {
177
- exclude.push('initializers/debug.js');
178
- }
179
-
180
- return new Funnel(tree, { exclude });
181
160
  }
182
161
  };
183
-
184
- function findImporter(addon) {
185
- if (typeof addon.import === 'function') {
186
- // If addon.import() is present (CLI 2.7+) use that
187
- return addon;
188
- } else {
189
- // Otherwise, reuse the _findHost implementation that would power addon.import()
190
- let current = addon;
191
- let app;
192
- do {
193
- app = current.app || app;
194
- } while (current.parent.parent && (current = current.parent));
195
- return app;
196
- }
197
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@html-next/vertical-collection",
3
- "version": "4.0.0-beta.1",
3
+ "version": "4.0.0-beta.2",
4
4
  "description": "infinite-scroll, done right. done.",
5
5
  "keywords": [
6
6
  "occlusion",
@@ -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.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.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.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
- };