Skip to content

Commit

Permalink
Merge pull request #237 from dhilt/issue-236-Access-to-min-max-Buffer…
Browse files Browse the repository at this point in the history
…-indexes

Access to Buffer indexes data via Adapter
  • Loading branch information
dhilt authored Dec 22, 2020
2 parents 6025b46 + d3122fb commit 46c54b6
Show file tree
Hide file tree
Showing 34 changed files with 364 additions and 191 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
Unlimited bidirectional scrolling over limited viewport. A directive for [Angular](https://angular.io/) framework. Built with [angular-library-starter](https://github.com/robisim74/angular-library-starter). Inspired by [angular-ui-scroll](https://github.com/angular-ui/ui-scroll) (AngularJS, since 2013). Demo is available at [dhilt.github.io/ngx-ui-scroll](https://dhilt.github.io/ngx-ui-scroll/).

<p dir="rtl">
<small>can donate? go <b><a href="https://github.com/dhilt/ngx-ui-scroll?sponsor=1">here</a></b> 👉 <br>make open-source world better</small>
</p>
<sub>can donate? go <b><a href="https://github.com/dhilt/ngx-ui-scroll?sponsor=1">here</a></b></sub><sub> 👉 <br>make open-source world better</sub></p>

- [Motivation](#motivation)
- [Features](#features)
Expand Down Expand Up @@ -168,6 +167,7 @@ Then `this.datasource.adapter.version`, `this.datasource.adapter.reload()` and o
|[isLoading](https://dhilt.github.io/ngx-ui-scroll/#adapter#is-loading)|boolean|Indicates whether the uiScroll is working ot not. |
|[isLoading$](https://dhilt.github.io/ngx-ui-scroll/#adapter#is-loading)|Subject&lt;boolean&gt;|An Observable version of "isLoading" property. |
|[itemsCount](https://dhilt.github.io/ngx-ui-scroll/#adapter#items-count)|number|A number of items that are rendered in the viewport at a moment.|
|[bufferInfo](https://dhilt.github.io/ngx-ui-scroll/#adapter#buffer-info)|IBufferInfo {<br>&nbsp;&nbsp;firstIndex:&nbsp;number;<br>&nbsp;&nbsp;lastIndex:&nbsp;number;<br>&nbsp;&nbsp;minIndex:&nbsp;number;<br>&nbsp;&nbsp;maxIndex:&nbsp;number;<br>&nbsp;&nbsp;absMinIndex:&nbsp;number;<br>&nbsp;&nbsp;absMaxIndex:&nbsp;number;<br>}|<li>firstIndex & lastIndex are the first and the last indexes in the current Buffer.</li><li>minIndex & maxIndex are min and max indexes that were present in the Buffer.</li><li>absMinIndex & absMaxIndex are min and max indexes that can be present in the Buffer.</li>|
|[bof](https://dhilt.github.io/ngx-ui-scroll/#adapter#bof-eof)|boolean|Indicates whether the beginning of the dataset is reached or not.|
|[bof$](https://dhilt.github.io/ngx-ui-scroll/#adapter#bof-eof)|Subject&lt;boolean&gt;|An Observable version of "bof" property.|
|[eof](https://dhilt.github.io/ngx-ui-scroll/#adapter#bof-eof)|boolean|Indicates whether the end of the dataset is reached or not.|
Expand Down
2 changes: 2 additions & 0 deletions demo/app/demos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { DemoResetComponent } from './samples/adapter/reset.component';
import { DemoReloadComponent } from './samples/adapter/reload.component';
import { DemoIsLoadingComponent } from './samples/adapter/is-loading.component';
import { DemoItemsCountComponent } from './samples/adapter/items-count.component';
import { DemoBufferInfoComponent } from './samples/adapter/buffer-info.component';
import { DemoBofEofComponent } from './samples/adapter/bof-eof.component';
import { DemoFirstLastVisibleItemsComponent } from './samples/adapter/first-last-visible-items.component';
import { DemoAppendPrependComponent } from './samples/adapter/append-prepend.component';
Expand Down Expand Up @@ -71,6 +72,7 @@ const adapter = [
DemoReloadComponent,
DemoIsLoadingComponent,
DemoItemsCountComponent,
DemoBufferInfoComponent,
DemoBofEofComponent,
DemoFirstLastVisibleItemsComponent,
DemoAppendPrependComponent,
Expand Down
5 changes: 5 additions & 0 deletions demo/app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ const adapterScope = {
name: 'Buffer items counter',
scope: globalScope.adapter.id
},
bufferInfo: {
id: 'buffer-info',
name: 'Buffer info',
scope: globalScope.adapter.id
},
bofEof: {
id: 'bof-eof',
name: 'Begin/end of file',
Expand Down
1 change: 1 addition & 0 deletions demo/app/samples/adapter.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ <h1>Angular UI Scroll Adapter Demos</h1>
<app-demo-is-loading></app-demo-is-loading>
<app-demo-is-loading-advanced></app-demo-is-loading-advanced>
<app-demo-items-count></app-demo-items-count>
<app-demo-buffer-info></app-demo-buffer-info>
<app-demo-bof-eof></app-demo-bof-eof>
<app-demo-first-last-visible-items></app-demo-first-last-visible-items>
<app-demo-check-size></app-demo-check-size>
Expand Down
25 changes: 25 additions & 0 deletions demo/app/samples/adapter/buffer-info.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<app-demo
[datasource]="datasource"
[context]="demoContext"
[sources]="sources"
>
<div actions>
firstIndex: {{datasource.adapter.bufferInfo.firstIndex}} /
lastIndex: {{datasource.adapter.bufferInfo.lastIndex}}<br>
minIndex: {{datasource.adapter.bufferInfo.minIndex}} /
maxIndex: {{datasource.adapter.bufferInfo.maxIndex}}<br>
</div>
<div description>
<p>
<em>Adapter.bufferInfo</em> is a read-only property which exposes
an object of the following type:
</p>
<pre>{{bufferInfoType}}</pre>
<p>
<em>firstIndex</em> & <em>lastIndex</em>
are the first and the last indexes in the current Buffer,
<em>minIndex</em> & <em>maxIndex</em> are min and max indexes that were present in the Buffer,
<em>absMinIndex</em> & <em>absMaxIndex</em> are min and max indexes that can be present in the Buffer.
</p>
</div>
</app-demo>
72 changes: 72 additions & 0 deletions demo/app/samples/adapter/buffer-info.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Component } from '@angular/core';

import { demos } from '../../routes';
import { DemoContext, DemoSources, DemoSourceType } from '../../shared/interfaces';
import { datasourceGetCallbackInfinite } from '../../shared/datasource-get';

import { Datasource } from '../../../../public_api'; // from 'ngx-ui-scroll';

@Component({
selector: 'app-demo-buffer-info',
templateUrl: './buffer-info.component.html'
})
export class DemoBufferInfoComponent {

demoContext: DemoContext = {
config: demos.adapter.map.bufferInfo,
viewportId: `buffer-info-viewport`,
count: 0,
log: ''
};

datasource = new Datasource({
get: datasourceGetCallbackInfinite(this.demoContext)
});

sources: DemoSources = [{
name: DemoSourceType.Component,
text: `datasource = new Datasource ({
get: (index, count, success) => {
const data = [];
for (let i = index; i <= index + count - 1; i++) {
data.push({ id: i, text: 'item #' + i });
}
success(data);
}
});`
}, {
active: true,
name: DemoSourceType.Template,
text: `firstIndex: {{datasource.adapter.bufferInfo.firstIndex}} /
lastIndex: {{datasource.adapter.bufferInfo.lastIndex}} <br>
minIndex: {{datasource.adapter.bufferInfo.minIndex}} /
maxIndex: {{datasource.adapter.bufferInfo.maxIndex}} <br>
<div class="viewport">
<div *uiScroll="let item of datasource">
<div class="item">{{item.text}}</div>
</div>
</div>`
}, {
name: DemoSourceType.Styles,
text: `.viewport {
width: 150px;
height: 250px;
overflow-y: auto;
}
.item {
font-weight: bold;
height: 25px;
}`
}];

bufferInfoType = `interface IBufferInfo {
firstIndex: number;
lastIndex: number;
minIndex: number;
maxIndex: number;
absMinIndex: number;
absMaxIndex: number;
}`;

}
2 changes: 1 addition & 1 deletion package-dist.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngx-ui-scroll",
"version": "1.11.0",
"version": "1.11.1",
"description": "Infinite/virtual scroll for Angular",
"main": "./bundles/ngx-ui-scroll.umd.js",
"module": "./fesm5/ngx-ui-scroll.js",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"deploy-app": "npm run build-app && firebase deploy",
"preinstall": "cd server && npm install",
"postinstall": "npm run build-app",
"pack:install": "npm run build && npm pack ./dist && npm install ngx-ui-scroll-1.11.0.tgz --no-save",
"pack:install": "npm run build && npm pack ./dist && npm install ngx-ui-scroll-1.11.1.tgz --no-save",
"pack:start": "npm run pack:install && npm start",
"build": "node build.js",
"publish:lib": "npm run build && npm publish ./dist",
Expand Down
45 changes: 34 additions & 11 deletions src/component/classes/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
ScrollerWorkflow,
IDatasourceOptional,
ProcessSubject,
IBufferInfo,
} from '../interfaces/index';

const ADAPTER_PROPS_STUB = ADAPTER_PROPS(EMPTY_ITEM);
Expand Down Expand Up @@ -56,6 +57,12 @@ const convertRemoveArgs = (options: AdapterRemoveOptions | ItemsPredicate) => {
return options;
};

const adapterMethodPreResult: AdapterMethodResult = {
success: false,
immediate: true,
details: 'Adapter is not initialized'
};

export class Adapter implements IAdapter {
private logger: Logger;
private getWorkflow: WorkflowGetter;
Expand Down Expand Up @@ -91,29 +98,31 @@ export class Adapter implements IAdapter {
eof: boolean;
eof$: Subject<boolean>;
itemsCount: number;
bufferInfo: IBufferInfo;

private relax$: Subject<boolean | AdapterMethodResult> | null;
private initialized: boolean;
private relax$: Subject<AdapterMethodResult> | null;
private relaxRun: Promise<AdapterMethodResult> | null;

private getPromisifiedMethod(method: Function) {
return (...args: any[]) =>
return (...args: any[]): Promise<AdapterMethodResult> =>
new Promise(resolve => {
if (this.relax$) {
this.relax$.pipe(
filter(value => !!value),
take(1)
).subscribe(value => resolve(value));
this.relax$
.pipe(take(1))
.subscribe(value => resolve(value));
}
method.apply(this, args);
if (!this.relax$) {
resolve();
if (!this.initialized) {
resolve(adapterMethodPreResult);
}
});
}

constructor(publicContext: IAdapter | null, getWorkflow: WorkflowGetter, logger: Logger) {
this.getWorkflow = getWorkflow;
this.logger = logger;
this.initialized = false;
this.relax$ = null;
this.relaxRun = null;
this.reloadCounter = 0;
Expand Down Expand Up @@ -218,6 +227,16 @@ export class Adapter implements IAdapter {
Object.defineProperty(this.demand, 'itemsCount', {
get: () => buffer.getVisibleItemsCount()
});
Object.defineProperty(this.demand, 'bufferInfo', {
get: (): IBufferInfo => ({
firstIndex: buffer.firstIndex,
lastIndex: buffer.lastIndex,
minIndex: buffer.minIndex,
maxIndex: buffer.maxIndex,
absMinIndex: buffer.absMinIndex,
absMaxIndex: buffer.absMaxIndex,
})
});
this.bof = buffer.bof;
buffer.bofSource.pipe(takeUntil(dispose$)).subscribe(value => this.bof = value);
this.eof = buffer.eof;
Expand All @@ -229,13 +248,12 @@ export class Adapter implements IAdapter {
// self-pending
if (onAdapterRun$) { // passed on the very first init only
if (!this.relax$) {
this.relax$ = new Subject<boolean | AdapterMethodResult>();
this.relax$ = new Subject<AdapterMethodResult>();
}
const relax$ = this.relax$;
onAdapterRun$.subscribe(({ status, payload }) => {
let isLoadingSub;
if (status === ProcessStatus.start) {
relax$.next(false);
isLoadingSub = this.isLoading$
.pipe(filter(isLoading => !isLoading), take(1), takeUntil(dispose$))
.subscribe(() => relax$.next({ success: true, immediate: false, details: null }));
Expand All @@ -252,6 +270,8 @@ export class Adapter implements IAdapter {
}
});
}

this.initialized = true;
}

dispose() {
Expand Down Expand Up @@ -383,9 +403,12 @@ export class Adapter implements IAdapter {
};
}

relax(callback?: Function): any {
relax(callback?: Function): Promise<AdapterMethodResult> {
const reloadId = this.reloadId;
this.logger.logAdapterMethod('relax', callback, ` of ${reloadId}`);
if (!this.initialized) {
return Promise.resolve(adapterMethodPreResult);
}
return this.relaxRun = this.relaxRun
? this.relaxRun.then(() => this.relaxUnchained(callback, reloadId))
: this.relaxUnchained(callback, reloadId).then((result) => {
Expand Down
20 changes: 9 additions & 11 deletions src/component/classes/adapter/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,23 @@ export const EMPTY_ITEM = {
} as ItemAdapter;

export class AdapterContext {
id: number;
mock: boolean;

constructor(mock: boolean) {
const id = ++instanceCount;
const conf = { configurable: !mock };

// set up permanent props
Object.defineProperty(this, 'id', { get: () => id, ...conf });
Object.defineProperty(this, 'mock', { get: () => mock, ...conf });
Object.defineProperty(this, 'version', { get: () => version, ...conf });

// props will be reassigned on Scroller instantiation
// set up props that will be reassigned on Scroller instantiation
ADAPTER_PROPS(EMPTY_ITEM)
.filter(({ permanent }) => !permanent)
.forEach(({ name, value, type }) =>
.forEach(({ name, value }) =>
Object.defineProperty(this, name, {
get: () => value,
configurable: !mock
...conf
})
);

// set up permanent props
Object.defineProperty(this, 'id', { get: () => id, configurable: !mock });
Object.defineProperty(this, 'mock', { get: () => mock, configurable: !mock });
Object.defineProperty(this, 'version', { get: () => version, configurable: !mock });
}
}
17 changes: 16 additions & 1 deletion src/component/classes/adapter/props.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { BehaviorSubject, Subject } from 'rxjs';

import { AdapterPropType as Prop, IAdapterProp } from '../../interfaces/index';
import { AdapterPropType as Prop, IAdapterProp, IBufferInfo } from '../../interfaces/index';

const noop = () => null;

const bufferInfoDefault: IBufferInfo = {
firstIndex: NaN,
lastIndex: NaN,
minIndex: NaN,
maxIndex: NaN,
absMinIndex: -Infinity,
absMaxIndex: +Infinity,
};

export const ADAPTER_PROPS = (nullItem: any): IAdapterProp[] => [
{
type: Prop.Scalar,
Expand Down Expand Up @@ -67,6 +76,12 @@ export const ADAPTER_PROPS = (nullItem: any): IAdapterProp[] => [
value: 0,
onDemand: true
},
{
type: Prop.Scalar,
name: 'bufferInfo',
value: bufferInfoDefault,
onDemand: true
},
{
type: Prop.WorkflowRunner,
name: 'reset',
Expand Down
8 changes: 4 additions & 4 deletions src/component/classes/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,12 @@ export class Buffer {
return isFinite(this.cache.maxIndex) ? this.cache.maxIndex : this.startIndex;
}

get firstIndex(): number | null {
return this.items.length ? this.items[0].$index : null;
get firstIndex(): number {
return this.items.length ? this.items[0].$index : NaN;
}

get lastIndex(): number | null {
return this.items.length ? this.items[this.items.length - 1].$index : null;
get lastIndex(): number {
return this.items.length ? this.items[this.items.length - 1].$index : NaN;
}

get finiteAbsMinIndex(): number {
Expand Down
2 changes: 1 addition & 1 deletion src/component/classes/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class Logger {
};
this.getFetchRange = (): string => {
const { first: { index: first }, last: { index: last } } = scroller.state.fetch;
return first !== null && last !== null && !Number.isNaN(first) && !Number.isNaN(last)
return !Number.isNaN(first) && !Number.isNaN(last)
? `[${first}..${last}]`
: 'no';
};
Expand Down
Loading

0 comments on commit 46c54b6

Please sign in to comment.