Skip to content

Commit

Permalink
Merge branch 'master' into issue-171-adapter-replace
Browse files Browse the repository at this point in the history
  • Loading branch information
dhilt committed Nov 27, 2020
2 parents 100fcc4 + 8af76d1 commit b7c7743
Show file tree
Hide file tree
Showing 52 changed files with 1,113 additions and 504 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,9 @@ Below is the list of invocable methods of the Adapter API with description and l
|[append](https://dhilt.github.io/ngx-ui-scroll/#/adapter#append-prepend)|(options: {<br>&nbsp;&nbsp;items:&nbsp;any[],<br>&nbsp;&nbsp;eof?:&nbsp;boolean<br>}) <br><br> (items:&nbsp;any&nbsp;&vert;&nbsp;any[], eof?:&nbsp;boolean) &#42;<br><sub>&#42; old signature, deprecated</sub>|Adds items to the end of the uiScroll dataset. If eof parameter is not set, items will be added and rendered immediately, they will be placed right after the last item in the uiScroll buffer. If eof parameter is set to true, items will be added and rendered only if the end of the dataset is reached; otherwise, these items will be virtualized. See also [bof/eof](https://dhilt.github.io/ngx-ui-scroll/#/adapter#bof-eof) demo. |
|[prepend](https://dhilt.github.io/ngx-ui-scroll/#/adapter#append-prepend)|(options: {<br>&nbsp;&nbsp;items:&nbsp;any[],<br>&nbsp;&nbsp;bof?:&nbsp;boolean<br>}) <br><br> (items:&nbsp;any&nbsp;&vert;&nbsp;any[], bof?:&nbsp;boolean) &#42;<br><sub>&#42; old signature, deprecated</sub>|Adds items to the beginning of the uiScroll dataset. If bof parameter is not set, items will be added and rendered immediately, they will be placed right before the first item in the uiScroll buffer. If bof parameter is set to true, items will be added and rendered only if the beginning of the dataset is reached; otherwise, these items will be virtualized. See also [bof/eof](https://dhilt.github.io/ngx-ui-scroll/#/adapter#bof-eof) demo. |
|[check](https://dhilt.github.io/ngx-ui-scroll/#/adapter#check-size)| |Checks if any of current items changed it's size and runs a procedure to provide internal consistency and new items fetching if needed. |
|[remove](https://dhilt.github.io/ngx-ui-scroll/#/adapter#remove)|(predicate:&nbsp;ItemsPredicate)<br><br>type&nbsp;ItemsPredicate&nbsp;=<br>&nbsp;&nbsp;(item: ItemAdapter)&nbsp;=><br>&nbsp;&nbsp;&nbsp;&nbsp;boolean|Removes items from current buffer. Predicate is a function to be applied to every item presently in the buffer. Predicate must return boolean value. If predicate's return value is true, the item will be removed. _Note!_ Current implementation allows to remove only a continuous series of items per call. If you want to remove, say, 5 and 7 items, you should call the remove method twice. Removing a series of items from 5 to 7 could be done in a single call. |
|[remove](https://dhilt.github.io/ngx-ui-scroll/#/adapter#remove)|(options: {<br>&nbsp;&nbsp;predicate?:&nbsp;ItemsPredicate,<br>&nbsp;&nbsp;indexes?:&nbsp;number[],<br>&nbsp;&nbsp;increase?:&nbsp;boolean<br>}) <br><br>(func:&nbsp;ItemsPredicate) &#42;<br><sub>&#42; old signature, deprecated</sub><br><br> type&nbsp;ItemsPredicate&nbsp;=<br>&nbsp;&nbsp;(item: ItemAdapter)&nbsp;=><br>&nbsp;&nbsp;&nbsp;&nbsp;boolean|Removes items form buffer and/or virtually. Predicate is a function to be applied to every item presently in the buffer. Predicate must return a boolean value. If predicate's return value is true, the item will be removed. Alternatively, if _indexes_ array is passed, the items whose indexes match the list will be removed. Only one of the _predicate_ and _indexes_ options is allowed. In case of _indexes_, the deletion is performed also virtually. By default, indexes of the items following the deleted ones are decremented. Instead, if _increase_ is set to _true_, indexes of the items before the removed ones will be increased. |
|[clip](https://dhilt.github.io/ngx-ui-scroll/#/adapter#clip)|(options: {<br>&nbsp;&nbsp;forwardOnly?:&nbsp;boolean,<br>&nbsp;&nbsp;backwardOnly?:&nbsp;boolean<br>})|Removes out-of-viewport items on demand. The direction in which invisible items should be clipped can be specified by passing an options object. If no options is passed (or both properties are set to _true_), clipping will occur in both directions. |
|[insert](https://dhilt.github.io/ngx-ui-scroll/#/adapter#insert)|(options: {<br>&nbsp;&nbsp;items:&nbsp;any[],<br>&nbsp;&nbsp;before?:&nbsp;ItemsPredicate,<br>&nbsp;&nbsp;after?:&nbsp;ItemsPredicate,<br>&nbsp;&nbsp;decrease?:&nbsp;boolean<br>})|Inserts items _before_ or _after_ the one that satisfies the predicate condition. Only one of _before_ and _after_ options is allowed. Indexes increase by default. Decreasing strategy can be enabled via _decrease_ option. |
|[insert](https://dhilt.github.io/ngx-ui-scroll/#/adapter#insert)|(options: {<br>&nbsp;&nbsp;items:&nbsp;any[],<br>&nbsp;&nbsp;before?:&nbsp;ItemsPredicate,<br>&nbsp;&nbsp;after?:&nbsp;ItemsPredicate,<br>&nbsp;&nbsp;decrease?:&nbsp;boolean<br>})|Inserts items _before_ or _after_ the one that presents in the buffer and satisfies the predicate condition. Only one of the _before_ and _after_ options is allowed. Indexes increase by default. Decreasing strategy can be enabled via _decrease_ option. |

Along with the documented API there are some undocumented features that can be treated as experimental. They are not tested enough and might change over time. Some of them can be found on the [experimental tab](https://dhilt.github.io/ngx-ui-scroll/#/experimental) of the demo app.

Expand All @@ -193,7 +193,7 @@ All of the Adapter methods return Promise resolving at the moment when the scrol
const { adapter } = this.datasource;
const predicate = ({ $index }) => $index === indexToReplace;
await adapter.relax();
await adapter.remove(predicate);
await adapter.remove({ predicate });
await adapter.insert({ items: [itemToReplace], before: predicate });
console.log('Replacement done');
```
Expand Down
45 changes: 25 additions & 20 deletions demo/app/samples/adapter/adapter-relax.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ export class DemoAdapterRelaxComponent {
text: `async doReplace() {
const { adapter } = this.datasource;
await adapter.relax();
adapter.remove(({ $index }) =>
$index > 4 && $index < 8
);
adapter.remove({
predicate: ({ $index }) => $index > 4 && $index < 8
});
await adapter.relax();
adapter.insert({
items: [{ id: '5*', text: 'item #5 *' }],
after: ({ $index }) => $index === 4 }
);
after: ({ $index }) => $index === 4
});
}
`
}, {
}, {
name: 'Is loading',
text: `relax(cb: Function) {
const { adapter } = this.datasource;
Expand All @@ -58,9 +58,9 @@ export class DemoAdapterRelaxComponent {
doReplace() {
const { adapter } = this.datasource;
this.relax(() =>
adapter.remove(({ $index }) =>
$index > 4 && $index < 8
)
adapter.remove({
predicate: ({ $index }) => $index > 4 && $index < 8
})
);
this.relax(() =>
adapter.insert({
Expand All @@ -70,14 +70,14 @@ doReplace() {
);
}
`
}, {
}, {
name: 'Callback',
text: `doReplace() {
const { adapter } = this.datasource;
adapter.relax(() =>
adapter.remove(({ $index }) =>
$index > 4 && $index < 8
)
adapter.remove({
predicate: ({ $index }) => $index > 4 && $index < 8
})
);
adapter.relax(() =>
adapter.insert({
Expand All @@ -87,21 +87,21 @@ doReplace() {
);
}
`
}, {
}, {
name: 'Return',
text: `async doReplace() {
const { adapter } = this.datasource;
await adapter.relax();
await adapter.remove(({ $index }) =>
$index > 4 && $index < 8
);
await adapter.remove({
predicate: ({ $index }) => $index > 4 && $index < 8
});
adapter.insert({
items: [{ id: '5*', text: 'item #5 *' }],
after: ({ $index }) => $index === 4
});
}
`
}];
}];

isLoadingSample = `
if (!adapter.isLoading) {
Expand All @@ -120,9 +120,14 @@ doReplace() {
const { adapter } = this.datasource;
const newItem = { id: '5*', text: 'item #5 *' };
await adapter.relax();
adapter.remove(({ $index }) => $index > 4 && $index < 8);
adapter.remove({
predicate: ({ $index }) => $index > 4 && $index < 8
});
await adapter.relax();
adapter.insert({ items: [newItem], after: ({ $index }) => $index === 4 });
adapter.insert({
items: [newItem],
after: ({ $index }) => $index === 4
});
}

}
2 changes: 1 addition & 1 deletion demo/app/samples/adapter/adapter-return-value.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class DemoAdapterReturnValueComponent {
}>`;

returnValueSample = `
const { immediate } = await adapter.remove(predicate);
const { immediate } = await adapter.remove({ predicate });
if (immediate) {
console.log('No items were removed');
}`;
Expand Down
2 changes: 1 addition & 1 deletion demo/app/samples/adapter/check-size.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
In this demo method <em>doCheck</em> is implemented to
</p>
<ul>
<li>change real size of items (from 15 to 25) in DOM;</li>
<li>change real size of 10 items (from 15 to 25) in DOM;</li>
<li>run <em>Adapter.check()</em>;</li>
<li>autoscroll to item that was first visible before change (optionally).</li>
</ul>
Expand Down
114 changes: 46 additions & 68 deletions demo/app/samples/adapter/check-size.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,22 @@ datasource = new Datasource ({
}
});
findElement(index: number) {
findElement(index: number): HTMLElement | null {
const viewportElement = document.getElementById(this.demoContext.viewportId);
return viewportElement
? viewportElement.querySelector(\`[data-sid="\${index}"]\`)
: null;
}
doChangeSize() {
const index = Number(this.startIndex - 5);
const DELTA = 5;
const index = Number(this.startIndex - DELTA);
if (!isNaN(index)) {
for (let i = index; i < index + 10; i++) {
for (let i = index; i < index + DELTA * 2; i++) {
const element = this.findElement(i);
if (element) {
(<HTMLElement>element).style.height = this.sizeValue + 'px';
const item = this.data.find(_item => _item.id === i);
element.style.height = this.sizeValue + 'px';
const item = this.data.find(({ id }) => id === i);
if (item) {
item.height = this.sizeValue;
}
Expand All @@ -108,41 +109,29 @@ doChangeSize() {
}
autoscroll(index: number) {
const { adapter } = this.datasource;
let isLoadingSubscription: Subscription;
const done = () => isLoadingSubscription && isLoadingSubscription.unsubscribe();
isLoadingSubscription = adapter.isLoading$.subscribe(isLoading => {
if (isLoading) {
return;
}
const element = this.findElement(index);
const viewportElement = document.getElementById(this.demoContext.viewportId);
if (!element || !viewportElement) {
done();
return;
}
const elementTop = element.getBoundingClientRect().top;
const viewportTop = viewportElement.getBoundingClientRect().top;
const toScroll = viewportTop - elementTop;
const diff = viewportElement.scrollTop - toScroll;
if (viewportElement.scrollTop === diff) {
done();
} else {
adapter.fix({ scrollPosition: diff });
if (viewportElement.scrollTop === diff) {
done();
}
}
});
const element = this.findElement(index);
const viewportElement = document.getElementsByClassName('viewport')[0];
if (!element || !viewportElement) {
return;
}
const elementTop = element.getBoundingClientRect().top;
const viewportTop = viewportElement.getBoundingClientRect().top;
const toScroll = viewportTop - elementTop;
const diff = viewportElement.scrollTop - toScroll;
if (viewportElement.scrollTop === diff) {
return;
}
this.datasource.adapter.fix({ scrollPosition: diff });
}
doCheck() {
let firstVisibleIndex;
async doCheck() {
await this.datasource.adapter.relax();
let firstVisibleIndex: number | undefined;
if (this.needAutoscroll) {
firstVisibleIndex = this.datasource.adapter.firstVisible.$index;
}
this.doChangeSize();
this.datasource.adapter.check();
await this.datasource.adapter.check();
if (firstVisibleIndex !== undefined) {
this.autoscroll(firstVisibleIndex);
}
Expand Down Expand Up @@ -175,21 +164,22 @@ First visible item's index: {{datasource.adapter.firstVisible.$index}}
}`
}];

findElement(index: number) {
findElement(index: number): HTMLElement | null {
const viewportElement = document.getElementById(this.demoContext.viewportId);
return viewportElement
? viewportElement.querySelector(`[data-sid="${index}"]`)
: null;
}

doChangeSize() {
const index = Number(this.startIndex - 5);
const DELTA = 5;
const index = Number(this.startIndex - DELTA);
if (!isNaN(index)) {
for (let i = index; i < index + 10; i++) {
for (let i = index; i < index + DELTA * 2; i++) {
const element = this.findElement(i);
if (element) {
(<HTMLElement>element).style.height = this.sizeValue + 'px';
const item = this.data.find(_item => _item.id === i);
element.style.height = this.sizeValue + 'px';
const item = this.data.find(({ id }) => id === i);
if (item) {
item.height = this.sizeValue;
}
Expand All @@ -199,41 +189,29 @@ First visible item's index: {{datasource.adapter.firstVisible.$index}}
}

autoscroll(index: number) {
const { adapter } = this.datasource;
let isLoadingSubscription: Subscription;
const done = () => isLoadingSubscription && isLoadingSubscription.unsubscribe();
isLoadingSubscription = adapter.isLoading$.subscribe(isLoading => {
if (isLoading) {
return;
}
const element = this.findElement(index);
const viewportElement = document.getElementById(this.demoContext.viewportId);
if (!element || !viewportElement) {
done();
return;
}
const elementTop = element.getBoundingClientRect().top;
const viewportTop = viewportElement.getBoundingClientRect().top;
const toScroll = viewportTop - elementTop;
const diff = viewportElement.scrollTop - toScroll;
if (viewportElement.scrollTop === diff) {
done();
} else {
adapter.fix({ scrollPosition: diff });
if (viewportElement.scrollTop === diff) {
done();
}
}
});
const element = this.findElement(index);
const viewportElement = document.getElementById(this.demoContext.viewportId);
if (!element || !viewportElement) {
return;
}
const elementTop = element.getBoundingClientRect().top;
const viewportTop = viewportElement.getBoundingClientRect().top;
const toScroll = viewportTop - elementTop;
const diff = viewportElement.scrollTop - toScroll;
if (viewportElement.scrollTop === diff) {
return;
}
this.datasource.adapter.fix({ scrollPosition: diff });
}

doCheck() {
let firstVisibleIndex;
async doCheck() {
await this.datasource.adapter.relax();
let firstVisibleIndex: number | undefined;
if (this.needAutoscroll) {
firstVisibleIndex = this.datasource.adapter.firstVisible.$index;
}
this.doChangeSize();
this.datasource.adapter.check();
await this.datasource.adapter.check();
if (firstVisibleIndex !== void 0) {
this.autoscroll(firstVisibleIndex);
}
Expand Down
5 changes: 5 additions & 0 deletions demo/app/samples/adapter/clip.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<p>
<em>Adapter.clip</em> is another <em>Adapter</em> API method
allowing to remove out-of-viewport items on demand.
Not to be confused with <em>Adapter.remove</em>, as
this method has no impact on the <em>Datasource</em>.
It just cleans up the DOM.
</p>
<p>
Commonly, the <em>uiScroll</em> runs the clipping procedure
each time new items are fetched after scrolling.
Also, the <em>uiScroll</em> clips old items from a side of the viewport
Expand Down
Loading

0 comments on commit b7c7743

Please sign in to comment.