Skip to content

Commit

Permalink
Merge pull request #157 from dhilt/issue-156-scroll-to-item-via-Adapt…
Browse files Browse the repository at this point in the history
…er-fix

Scroll to item via Adapter.fix
  • Loading branch information
dhilt authored Apr 24, 2020
2 parents 1abb97d + 8908f21 commit 1f4af27
Show file tree
Hide file tree
Showing 27 changed files with 307 additions and 159 deletions.
2 changes: 2 additions & 0 deletions demo/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { DemoPagesDatasourceComponent } from './samples/datasource/pages-datasou
import { DemoAdapterFixPositionComponent } from './samples/experimental/adapter-fix-position.component';
import { DemoInverseSettingComponent } from './samples/experimental/inverse-setting.component';
import { DemoAdapterFixUpdaterComponent } from './samples/experimental/adapter-fix-updater.component';
import { DemoAdapterFixScrollToItemComponent } from './samples/experimental/adapter-fix-scrollToItem.component';
import { WindowComponent } from './samples/window.component';
import { TestComponent, TestInnerComponent } from './samples/test.component';

Expand Down Expand Up @@ -101,6 +102,7 @@ import { AppRoutingModule } from './app-routing.module';
DemoAdapterFixPositionComponent,
DemoInverseSettingComponent,
DemoAdapterFixUpdaterComponent,
DemoAdapterFixScrollToItemComponent,
WindowComponent,
TestComponent,
TestInnerComponent
Expand Down
1 change: 1 addition & 0 deletions demo/app/samples/experimental.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ <h1>Angular UI Scroll Experimental Demos</h1>
<app-adapter-fix-position></app-adapter-fix-position>
<app-inverse-setting></app-inverse-setting>
<app-adapter-fix-updater></app-adapter-fix-updater>
<app-adapter-fix-scroll-to-item></app-adapter-fix-scroll-to-item>
2 changes: 2 additions & 0 deletions demo/app/samples/experimental.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export class ExperimentalComponent {
minIndex?: number;
maxIndex?: number;
updater?: (item: ItemAdapter) => any;
scrollToItem?: (item: ItemAdapter) => boolean;
scrollToItemOpt?: boolean | ScrollIntoViewOptions;
}`;

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ export class DemoAdapterFixPositionComponent {
data.push({ id: i, text: 'item #' + i });
}
success(data);
},
devSettings: {
debug: true,
// logProcessRun: true
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<app-demo [datasource]="datasource" [context]="demoContext" [sources]="sources">

<div actions>
<div class="comment">Available since v1.6.2, undocumented</div>
<button (click)="doScrollTo()">Scroll to</button> item #
<input [(ngModel)]="index" size="2">
<br>
<input type="checkbox" [(ngModel)]="scrollToBottom"> - scroll to bottom
</div>

<div description>
<p>
What if we want to scroll to a specific item in the viewport?
This can be done via <em>scrollToItem</em> option of the <em>Adapter.fix</em> method.
This option specifies an item predicate function, and the first item in the Buffer,
that satisfies this predicate, will be the item to which the viewport will scroll.
</p>
<p>
Scrolling is performed via <em>element.scrollIntoView()</em> native DOM Element interface's method.
According to its specification, it accepts 1 optional argument which can be a Boolean parameter or
special Object parameter (details can be found on
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView">MDN</a>).
To provide this option, the <em>Adapter.fix</em> method handles another property of its argument object:
<em>scrollToItemOpt</em>. The demo shows how to use it.
</p>
</div>
</app-demo>
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Component } from '@angular/core';

import { DemoContext, DemoSources, DemoSourceType } from '../../shared/interfaces';
import { Datasource } from '../../../../public_api';
import { doLog } from '../../shared/datasource-get';

@Component({
selector: 'app-adapter-fix-scroll-to-item',
templateUrl: './adapter-fix-scrollToItem.component.html'
})
export class DemoAdapterFixScrollToItemComponent {

demoContext: DemoContext = <DemoContext>{
scope: 'experimental',
title: `Adapter fix scrollToItem`,
titleId: `adapter-fix-scrollToItem`,
noInfo: true
};

datasource = new Datasource({
get: (index, count, success) => {
const data = [];
for (let i = index; i < index + count; i++) {
data.push({ id: i, text: 'item #' + i });
}
success(data);
},
devSettings: {
debug: true
}
});

index: string;
scrollToBottom: boolean;

constructor() {
this.index = '5';
this.scrollToBottom = false;
}

sources: DemoSources = [{
name: DemoSourceType.Template,
text: `<button (click)="doScrollTo()">Scroll to</button> item #
<input [(ngModel)]="index" size="2">
<input type="checkbox" [(ngModel)]="scrollToBottom"> - scroll to bottom
<div class="viewport">
<div *uiScroll="let item of datasource">
<div class="item">{{item.text}}</div>
</div>
</div>`
}, {
name: DemoSourceType.Component,
text: `index = '5'
scrollToBottom = false;
datasource = new Datasource({
get: (index, count, success) => {
const data = [];
for (let i = index; i < index + count; i++) {
data.push({ id: i, text: 'item #' + i });
}
success(data);
}
});
doScrollTo() {
const index = Number(this.index);
const alignToTop = !Boolean(this.scrollToBottom);
if (!isNaN(index)) {
this.datasource.adapter.fix({
scrollToItem: ({ data }) => data.id === index,
scrollToItemOpt: alignToTop
});
}
}
`
}];

adapterFixUpdater = `Adapter.fix({ scrollToItem })`;

doScrollTo() {
const index = Number(this.index);
const alignToTop = !Boolean(this.scrollToBottom);
if (!isNaN(index)) {
this.datasource.adapter.fix({
scrollToItem: ({ data }) => data.id === index,
scrollToItemOpt: alignToTop
});
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ export class DemoAdapterFixUpdaterComponent {
data.push({ id: i, text: 'item #' + i });
}
success(data);
},
devSettings: {
debug: true,
// logProcessRun: true
}
});

Expand Down
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.6.1",
"version": "1.6.2",
"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 @@ -20,7 +20,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.6.1.tgz --no-save",
"pack:install": "npm run build && npm pack ./dist && npm install ngx-ui-scroll-1.6.2.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
5 changes: 5 additions & 0 deletions src/component/classes/domRoutines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,9 @@ export class Routines {
return this.horizontal ? element.offsetLeft : element.offsetTop;
}

scrollTo(element: HTMLElement, argument?: boolean | ScrollIntoViewOptions) {
this.checkElement(element);
element.scrollIntoView(argument);
}

}
6 changes: 6 additions & 0 deletions src/component/classes/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ export class Item {
}
}

scrollTo(argument?: boolean | ScrollIntoViewOptions) {
if (this.element) {
this.routines.scrollTo(this.element, argument);
}
}

updateIndex(index: number) {
this.$index = index;
this.nodeId = String(index);
Expand Down
11 changes: 10 additions & 1 deletion src/component/inputs/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const {
FUNC_WITH_X_AND_MORE_ARGUMENTS,
ONE_OF_MUST,
ONE_OF_CAN,
OR,
} = VALIDATORS;

const RESET_METHOD_PARAMS: ICommonProps<DatasourceProps> = {
Expand Down Expand Up @@ -91,6 +92,8 @@ enum AdapterFixParams {
minIndex = 'minIndex',
maxIndex = 'maxIndex',
updater = 'updater',
scrollToItem = 'scrollToItem',
scrollToItemOpt = 'scrollToItemOpt',
}

const FIX_METHOD_PARAMS: ICommonProps<AdapterFixParams> = {
Expand All @@ -105,7 +108,13 @@ const FIX_METHOD_PARAMS: ICommonProps<AdapterFixParams> = {
},
[AdapterFixParams.updater]: {
validators: [FUNC_WITH_X_ARGUMENTS(1)]
}
},
[AdapterFixParams.scrollToItem]: {
validators: [FUNC_WITH_X_ARGUMENTS(1)]
},
[AdapterFixParams.scrollToItemOpt]: {
validators: [OR([BOOLEAN, OBJECT])]
},
};

export const AdapterMethods = {
Expand Down
12 changes: 12 additions & 0 deletions src/component/inputs/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ const onOneOf = (tokens: string[], must: boolean) => (value: any, context: any):
return { value, isSet, isValid: !errors.length, errors };
};

const onOr = (validators: IValidator[]) => (value: any): ValidatedValue => {
const errors = [];
if (validators.every(validator => !validator.method(value).isValid)) {
errors.push(validators.map(v => v.type).join(' OR '));
}
return { value, isSet: true, isValid: !errors.length, errors };
};

export const VALIDATORS = {
NUMBER: {
type: ValidatorType.number,
Expand Down Expand Up @@ -215,6 +223,10 @@ export const VALIDATORS = {
type: ValidatorType.oneOfMust,
method: onOneOf(list, true)
}),
OR: (list: IValidator[]): IValidator => ({
type: ValidatorType.or,
method: onOr(list)
}),
};

export class ValidatedData implements IValidatedData {
Expand Down
3 changes: 2 additions & 1 deletion src/component/interfaces/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ export interface AdapterFixOptions {
minIndex?: number;
maxIndex?: number;
updater?: ItemsLooper;
safe?: boolean;
scrollToItem?: ItemsPredicate;
scrollToItemOpt?: boolean | ScrollIntoViewOptions;
}

export interface IAdapter {
Expand Down
1 change: 1 addition & 0 deletions src/component/interfaces/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export enum ValidatorType {
funcOfxAndMoreArguments = 'must have at least arg1 arguments',
oneOfCan = 'can be present only if none of arg1 list is present',
oneOfMust = 'must be present if none of arg1 list is present',
or = 'must satisfy at least 1 validator from arg1 list',
}

type ValidatorMethod = (value: any, context?: any) => ValidatedValue;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Scroller } from '../scroller';
import { Item } from '../classes/item';
import { Process, ProcessStatus } from '../interfaces/index';
import { Scroller } from '../../scroller';
import { Item } from '../../classes/item';
import { Process, ProcessStatus } from '../../interfaces/index';

export default class Append {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Scroller } from '../scroller';
import { Process, ProcessStatus } from '../interfaces/index';
import { Scroller } from '../../scroller';
import { Process, ProcessStatus } from '../../interfaces/index';

export default class Check {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Scroller } from '../scroller';
import { ADAPTER_METHODS, AdapterMethods, validate } from '../inputs/index';
import { AdapterClipOptions, Process, ProcessStatus } from '../interfaces/index';
import { Scroller } from '../../scroller';
import { ADAPTER_METHODS, AdapterMethods, validate } from '../../inputs/index';
import { AdapterClipOptions, Process, ProcessStatus } from '../../interfaces/index';

export default class UserClip {

Expand Down
Loading

0 comments on commit 1f4af27

Please sign in to comment.