Socket
Socket
Sign inDemoInstall

@zestia/ember-simple-infinite-scroller

Package Overview
Dependencies
397
Maintainers
3
Versions
47
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 7.0.5 to 8.0.0-beta.0

217

addon/components/infinite-scroller.js
import Component from '@glimmer/component';
import { debounce, cancel, scheduleOnce } from '@ember/runloop';
import { action } from '@ember/object';
import { resolve } from 'rsvp';
import { inject } from '@ember/service';
import { tracked } from '@glimmer/tracking';
const { round } = Math;
export default class InfiniteScrollerComponent extends Component {
@inject('-infinite-scroller') _infiniteScroller;
debug = false;
scroller = null;
debounceId = null;
@tracked error = null;
@tracked isLoading = false;
@tracked isScrollable = false;
element = null;
get selector() {
return this.args.selector || null;
get debounce() {
return this.args.debounce ?? 100;
}
get useDocument() {
return this.args.useDocument || false;
get percent() {
return this.args.percent ?? 100;
}
get scrollDebounce() {
return this.args.scrollDebounce || 100;
get normalisedScrollerElement() {
if (this.scroller instanceof Document) {
return this.scroller.documentElement;
} else {
return this.scroller;
}
}
get leeway() {
return parseInt(this.args.leeway || '0%', 10);
}
@action
handleInsertElement(element) {
this._registerElement(element);
if (!this.scroller) {
this._registerScroller(this.args.element ?? element);
}
this._scheduleCheckScrollable();
this._listen();
}

@@ -41,4 +42,3 @@

handleDestroyElement() {
this._stopListening();
this._deregisterElement();
this._deregisterScroller();
}

@@ -51,55 +51,44 @@

_registerElement(element) {
this.element = element;
@action
setElement(element) {
this._registerScroller(element);
}
_deregisterElement() {
this.element = null;
}
_isScrollable() {
let element = this._scroller();
if (this.useDocument) {
element = this._documentElement();
_registerScroller(element) {
if (this.scroller) {
this._stopListening();
}
if (!element) {
return;
}
this.scroller = element;
return element.scrollHeight > element.clientHeight;
this._startListening();
}
_scheduleCheckScrollable() {
scheduleOnce('afterRender', this, '_checkScrollable');
_deregisterScroller() {
this._stopListening();
cancel(this.debounceId);
this.scroller = null;
}
_checkScrollable() {
this.isScrollable = this._isScrollable();
}
_startListening() {
this._scrollHandler = this._handleScroll.bind(this);
_listen() {
this._scrollHandler = this._scroll.bind(this);
this._listener().addEventListener('scroll', this._scrollHandler);
this.scroller.addEventListener('scroll', this._scrollHandler);
}
_stopListening() {
this._listener().removeEventListener('scroll', this._scrollHandler);
cancel(this._scrollDebounceId);
this.scroller.removeEventListener('scroll', this._scrollHandler);
}
_scroll(e) {
this._scrollDebounceId = debounce(
this,
'_debouncedScroll',
e,
this.scrollDebounce
);
_handleScroll() {
this.debounceId = debounce(this, '_checkShouldLoadMore', this.debounce);
}
_debouncedScroll() {
if (this._shouldLoadMore()) {
_checkShouldLoadMore() {
const scrollState = this._getScrollState();
const shouldLoadMore = scrollState.reachedBottom && !this.isLoading;
this._debug({ ...scrollState, shouldLoadMore });
if (shouldLoadMore) {
this._loadMore();

@@ -109,84 +98,45 @@ }

_log() {
this._infiniteScroller.log(...arguments);
}
_checkScrollable() {
if (!this.scroller) {
return;
}
_document() {
return this._infiniteScroller.document;
}
const scrollState = this._getScrollState();
_documentElement() {
return this._infiniteScroller.documentElement;
}
this._debug({ ...scrollState });
_listener() {
if (this.useDocument) {
return this._document();
} else {
return this._scroller();
}
this.isScrollable = scrollState.isScrollable;
}
_scroller() {
if (this.selector) {
return this.element.querySelector(this.selector);
} else {
return this.element;
}
_scheduleCheckScrollable() {
scheduleOnce('afterRender', this, '_checkScrollable');
}
_shouldLoadMore() {
let state;
if (this.useDocument) {
state = this._detectBottomOfElementInDocument();
} else {
state = this._detectBottomOfElement();
_debug(state) {
if (!this.debug) {
return;
}
state.shouldLoadMore = state.reachedBottom && !this.isLoading;
this._log(state);
return state.shouldLoadMore;
console.table([state]); // eslint-disable-line
}
_detectBottomOfElementInDocument() {
const scroller = this._scroller();
const clientHeight = this._infiniteScroller.documentElement.clientHeight;
const bottom = scroller.getBoundingClientRect().bottom;
const leeway = this.leeway;
const pixelsToBottom = bottom - clientHeight;
const percentageToBottom = (pixelsToBottom / bottom) * 100;
const reachedBottom = percentageToBottom <= leeway;
return {
clientHeight,
bottom,
leeway,
pixelsToBottom,
percentageToBottom,
reachedBottom
};
}
_detectBottomOfElement() {
const scroller = this._scroller();
const scrollHeight = scroller.scrollHeight;
const scrollTop = scroller.scrollTop;
const clientHeight = scroller.clientHeight;
_getScrollState() {
const element = this.normalisedScrollerElement;
const scrollHeight = element.scrollHeight;
const scrollTop = element.scrollTop;
const clientHeight = element.clientHeight;
const isScrollable = scrollHeight > clientHeight;
const bottom = scrollHeight - clientHeight;
const leeway = this.leeway;
const pixelsToBottom = bottom - scrollTop;
const percentageToBottom = (pixelsToBottom / bottom) * 100;
const reachedBottom = percentageToBottom <= leeway;
const percent = this.percent;
const percentScrolled = round((scrollTop / bottom) * 100);
const reachedBottom = percentScrolled >= percent;
return {
isScrollable,
scrollHeight,
clientHeight,
scrollTop,
clientHeight,
bottom,
leeway,
pixelsToBottom,
percentageToBottom,
percent,
percentScrolled,
reachedBottom

@@ -196,22 +146,7 @@ };

_loadMore() {
const action = this.args.onLoadMore;
if (typeof action !== 'function') {
return;
}
this.error = null;
async _loadMore() {
this.isLoading = true;
resolve(action())
.catch(this._loadError.bind(this))
.finally(this._loadFinished.bind(this));
}
await this._invokeAction('onLoadMore');
_loadError(error) {
this.error = error;
}
_loadFinished() {
this.isLoading = false;

@@ -221,2 +156,10 @@

}
_invokeAction(name, ...args) {
const action = this.args[name];
if (typeof action === 'function') {
return action(...args);
}
}
}
# Changelog
## 8.0.0-beta.0
- Removes `@selector` in favour of `@element`
- Removes `@useDocument` in favour of `@element`
- Removes `scroller.error`
- Renames `@scrollDebounce` to `@debounce`
- Renames `@leeway` to `@percent`. This is the inverse!
- Adds `scroller.setElement` to make setting child elements easier
- Upgrades dependencies
## 7.0.7
- Fix division by zero
## 7.0.6
- Run ember-cli-update
## 7.0.5

@@ -4,0 +22,0 @@

{
"name": "@zestia/ember-simple-infinite-scroller",
"version": "7.0.5",
"version": "8.0.0-beta.0",
"description": "Simple infinite scroller component for Ember apps",

@@ -34,10 +34,11 @@ "directories": {

"@ember/optional-features": "^2.0.0",
"@zestia/ember-template-lint-plugin": "^3.0.7",
"@zestia/eslint-config": "^3.0.2",
"@ember/test-helpers": "^2.1.3",
"@zestia/ember-template-lint-plugin": "^3.0.9",
"@zestia/eslint-config": "^3.0.3",
"@zestia/prettier-config": "^1.0.4",
"@zestia/stylelint-config": "^2.0.44",
"@zestia/stylelint-config": "^2.0.45",
"babel-eslint": "^10.1.0",
"broccoli-asset-rev": "^3.0.0",
"ember-auto-import": "^1.7.0",
"ember-cli": "^3.22.0",
"ember-auto-import": "^1.10.0",
"ember-cli": "^3.23.0",
"ember-cli-dependency-checker": "^3.2.0",

@@ -47,25 +48,26 @@ "ember-cli-github-pages": "^0.2.2",

"ember-cli-sri": "^2.1.1",
"ember-cli-uglify": "^3.0.0",
"ember-cli-terser": "^4.0.0",
"ember-disable-prototype-extensions": "^1.1.3",
"ember-load-initializers": "^2.1.1",
"ember-load-initializers": "^2.1.2",
"ember-maybe-import-regenerator": "^0.1.6",
"ember-qunit": "^4.6.0",
"ember-qunit": "^5.1.1",
"ember-resolver": "^8.0.2",
"ember-source": "^3.22.0",
"ember-source": "^3.23.1",
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^2.14.0",
"ember-template-lint": "^2.15.0",
"ember-try": "^1.4.0",
"eslint": "^7.12.1",
"eslint": "^7.15.0",
"loader.js": "^4.7.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.2",
"qunit-dom": "^1.5.0",
"release-it": "^14.2.1",
"sass": "^1.29.0",
"stylelint": "^13.7.2"
"prettier": "^2.2.1",
"qunit": "^2.13.0",
"qunit-dom": "^1.6.0",
"release-it": "^14.2.2",
"sass": "^1.30.0",
"stylelint": "^13.8.0"
},
"dependencies": {
"@ember/render-modifiers": "^1.0.2",
"@glimmer/component": "^1.0.2",
"@glimmer/tracking": "^1.0.2",
"@glimmer/component": "^1.0.3",
"@glimmer/tracking": "^1.0.3",
"ember-cli-babel": "^7.23.0",

@@ -72,0 +74,0 @@ "ember-cli-htmlbars": "^5.3.1"

@@ -52,2 +52,3 @@ # @zestia/ember-simple-infinite-scroller

- Supports use with FastBoot ✔︎
- No included styles ✔︎

@@ -64,25 +65,20 @@ ## Configuration

<td>onLoadMore</td>
<td>Action to perform when the bottom is scrolled into view</td>
<td>Action to perform when the <code>@percent</code> is scrolled past</td>
<td><code>null</code></td>
</tr>
<tr>
<td>selector</td>
<td>Monitors the scrolling of a specific child element, e.g. <code>selector=".foo-bar"</code></td>
<td>element</td>
<td>Monitors the scroll position of the given element</td>
<td><code>null</code></td>
</tr>
<tr>
<td>useDocument</td>
<td>Monitors the document scroll position rather than the element's scroll position.</td>
<td><code>false</code></td>
<td>percent</td>
<td>Distance scroll from the top for when to fire the load more action</td>
<td><code>100</code></td>
</tr>
<tr>
<td>leeway</td>
<td>Percentage distance away from the bottom</td>
<td><code>"0%"</code></td>
<td>debounce</td>
<td>Milliseconds delay for when to check if more needs to be loaded</td>
<td><code>100</code></td>
</tr>
<tr>
<td>scrollDebounce</td>
<td>Milliseconds delay used to check if the bottom has been reached</td>
<td><code>100</code> ms</td>
</tr>
</table>

@@ -100,2 +96,6 @@

<tr>
<td>setElement</td>
<td>Sets the element for which to monitor the scroll position of</td>
</tr>
<tr>
<td>isLoading</td>

@@ -109,6 +109,2 @@ <td>True when the promise for more data has not resolved yet</td>

<tr>
<td>error</td>
<td>The caught error from the last attempt to load more</td>
</tr>
<tr>
<td>loadMore</td>

@@ -119,17 +115,2 @@ <td>Action for manually loading more</td>

## Element vs Document scroll
Either make your component scrollable:
```css
.my-element {
max-height: 300px;
overflow-y: auto;
}
```
**OR**
Set `@useDocument={{true}}` if your component is not scrollable.
## Performance

@@ -142,3 +123,3 @@

```javascript
customEvents: {
customEvents = {
touchstart: null,

@@ -148,9 +129,9 @@ touchmove: null,

touchcancel: null
}
};
```
## Other scenarios
## Scenario to be aware of
If your scrollable element is displaying 10 things, but they don't cause the element to overflow,
then the user won't ever be able to load more - because they won't be able to scroll and therefore
then the user won't ever be able to load more - because they won't be able to _scroll_ and therefore
the `onLoadMore` action will never fire.

@@ -166,10 +147,6 @@

{{#if this.hasMoreThings}}
{{#if scroller.isScrollable}}
Loading more...
{{else}}
<button {{on "click" scroller.loadMore}}>Load more</button>
{{/if}}
{{/if}}
{{#unless scroller.isScrollable}}
<button {{on "click" scroller.loadMore}}>Load more</button>
{{/unless}}
</InfiniteScroller>
```

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc