Skip to content

Commit

Permalink
allow scrolling up
Browse files Browse the repository at this point in the history
  • Loading branch information
amk221 committed Mar 8, 2024
1 parent a7062fc commit 635b9c0
Show file tree
Hide file tree
Showing 18 changed files with 182 additions and 79 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ https://zestia.github.io/ember-simple-infinite-scroller
## Example

```handlebars
<InfiniteScroller @onLoadMore={{this.loadMore}} as |scroller|>
<InfiniteScroller @onLoadMore={{this.loadMore}} @percentDown{{100}} as |scroller|>
{{#each things as |thing|}}
...
{{/each}}
Expand Down Expand Up @@ -64,13 +64,13 @@ Required. Fired when the the element has been scrolled to the specified `@percen

Optional. By default the scroll position of the component's own DOM element is monitored. You can use this argument to change the element, to monitor the document for example.

#### `@down`
#### `@percentDown`

Optional. The distance that has to be scrolled down before the load more action is fired.
Optional. The distance that has to be scrolled down before the load more action is fired. 100% means the bottom.

#### `@up`
#### `@percentUp`

Optional. The distance that has to be scrolled up before the load more action is fired.
Optional. The distance that has to be scrolled up before the load more action is fired. 0% means the top.

#### `@debounce`

Expand Down
38 changes: 25 additions & 13 deletions addon/components/infinite-scroller.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { debounce, cancel } from '@ember/runloop';
import { tracked } from '@glimmer/tracking';
import { modifier } from 'ember-modifier';
import { action } from '@ember/object';
import { isPresent } from '@ember/utils';
const { round } = Math;
const UP = 'UP';
const DOWN = 'DOWN';
Expand All @@ -13,7 +14,7 @@ export default class InfiniteScrollerComponent extends Component {
@tracked scrollState = {};
@tracked lastScrollState = {};

debug;
debug = true;
scroller;
debounceId;

Expand All @@ -30,12 +31,28 @@ export default class InfiniteScrollerComponent extends Component {
return this.args.debounce ?? 100;
}

get percent() {
return this.args.percent ?? 100;
}

get shouldLoadMore() {
return this.scrollState.reachedBottom && !this.isLoading;
if (this.isLoading) {
return false;
}

if (
isPresent(this.args.percentDown) &&
this.scrollState.direction === DOWN &&
this.scrollState.percentScrolled >= this.args.percentDown
) {
return true;
}

if (
isPresent(this.args.percentUp) &&
this.scrollState.direction === UP &&
this.scrollState.percentScrolled <= this.args.percentUp
) {
return true;
}

return false;
}

get normalisedScrollerElement() {
Expand Down Expand Up @@ -100,8 +117,6 @@ export default class InfiniteScrollerComponent extends Component {
}

this.scrollState = this._getScrollState();

this._debug();
}

_debug() {
Expand All @@ -124,21 +139,18 @@ export default class InfiniteScrollerComponent extends Component {
const clientHeight = element.clientHeight;
const isScrollable = scrollHeight > clientHeight;
const bottom = scrollHeight - clientHeight;
const percent = this.percent;
const percentScrolled = round((scrollTop / bottom) * 100);
const reachedBottom = percentScrolled >= percent;
const scrollingDown = element.scrollTop > this.lastScrollState.scrollTop;
const direction = scrollingDown ? DOWN : UP;
const scrollingUp = element.scrollTop < this.lastScrollState.scrollTop;
const direction = scrollingDown ? DOWN : scrollingUp ? UP : null;

return {
isScrollable,
scrollHeight,
clientHeight,
scrollTop,
bottom,
percent,
percentScrolled,
reachedBottom,
direction
};
}
Expand Down
33 changes: 21 additions & 12 deletions tests/dummy/app/_controllers/things.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,37 @@ export default class ThingsController extends Controller {
@injectController('application') appController;

@tracked page = 1;
@tracked things;
@tracked things = this._generateThingsForPage(1);
perPage = 10;

constructor() {
super(...arguments);
this.things = this._generateThings();
}

@action
handleLoadMore() {
handleLoadMore(direction) {
return new Promise((resolve) => {
later(() => {
this.page++;
this.things = [...this.things, ...this._generateThings()];
if (direction === 'DOWN' && this.page < 10) {
this.page++;
this.things = [
...this.things,
...this._generateThingsForPage(this.page)
];
}

if (direction === 'UP' && this.page > 1) {
this.page--;
this.things = [
...this._generateThingsForPage(this.page),
...this.things
];
}

resolve();
}, this.appController.loadDelay);
});
}

_generateThings() {
const start = this.things ? this.things.length + 1 : 0;
const end = this.page * this.perPage;
_generateThingsForPage(page) {
const start = (page - 1) * 10 + 1;
const end = page * this.perPage;
return generateThings(start, end);
}
}
12 changes: 12 additions & 0 deletions tests/dummy/app/controllers/example-5.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import ThingsController from 'dummy/_controllers/things';
import { modifier } from 'ember-modifier';
import { tracked } from '@glimmer/tracking';

export default class extends ThingsController {
@tracked page = 4;
@tracked things = this._generateThingsForPage(5);

scrollIntoView = modifier((element) => element.scrollIntoView(), {
eager: false
});
}
1 change: 1 addition & 0 deletions tests/dummy/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ Router.map(function () {
this.route('example-2');
this.route('example-3');
this.route('example-4');
this.route('example-5');
});
3 changes: 0 additions & 3 deletions tests/dummy/app/routes/application.js

This file was deleted.

3 changes: 0 additions & 3 deletions tests/dummy/app/routes/example-1.js

This file was deleted.

3 changes: 0 additions & 3 deletions tests/dummy/app/routes/example-2.js

This file was deleted.

3 changes: 0 additions & 3 deletions tests/dummy/app/routes/example-3.js

This file was deleted.

3 changes: 0 additions & 3 deletions tests/dummy/app/routes/example-4.js

This file was deleted.

4 changes: 4 additions & 0 deletions tests/dummy/app/templates/application.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
<LinkTo @route="example-4">
Example 4
</LinkTo>
|
<LinkTo @route="example-5">
Example 5
</LinkTo>

<p>
<label>
Expand Down
1 change: 1 addition & 0 deletions tests/dummy/app/templates/example-1.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<InfiniteScroller
class="example-1"
@percentDown={{100}}
@onLoadMore={{this.handleLoadMore}}
as |scroller|
>
Expand Down
3 changes: 2 additions & 1 deletion tests/dummy/app/templates/example-2.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<InfiniteScroller
class="example-2"
@percentDown={{100}}
@onLoadMore={{this.handleLoadMore}}
@element={{this.document}}
as |scroller|
Expand All @@ -19,7 +20,7 @@
{{/each}}

{{#unless scroller.isScrollable}}
<button type="button" {{on "click" scroller.loadMore}}>
<button type="button" {{on "click" (fn scroller.loadMore "DOWN")}}>
Load more
</button>
{{/unless}}
Expand Down
2 changes: 1 addition & 1 deletion tests/dummy/app/templates/example-3.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

<InfiniteScroller
class="example-3"
@percentDown={{50}}
@onLoadMore={{this.handleLoadMore}}
@debounce={{10}}
@percent={{50}}
as |scroller|
>
{{#each this.things as |thing|}}
Expand Down
1 change: 1 addition & 0 deletions tests/dummy/app/templates/example-4.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

<InfiniteScroller
class="example-4"
@percentDown={{100}}
@element={{this.scroller}}
@onLoadMore={{this.handleLoadMore}}
as |scroller|
Expand Down
21 changes: 21 additions & 0 deletions tests/dummy/app/templates/example-5.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<p>
An action is fired when infinite scroll component is scrolled to the very top
</p>

<InfiniteScroller
class="example-1"
@percentUp={{0}}
@onLoadMore={{this.handleLoadMore}}
as |scroller|
>
{{#if scroller.isLoading}}
<div class="status">
Loading...
</div>
{{/if}}
{{#each this.things as |thing|}}
<div class="thing" {{this.scrollIntoView}}>
{{thing.name}}
</div>
{{/each}}
</InfiniteScroller>
4 changes: 1 addition & 3 deletions tests/dummy/app/utils/generate-things.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { A as emberA } from '@ember/array';

export default function (start, end) {
const array = emberA();
const array = [];

for (let i = start; i <= end; i += 1) {
array.push({
Expand Down
Loading

0 comments on commit 635b9c0

Please sign in to comment.