Skip to content

Commit

Permalink
maintainance: updated position resolver and axis without factories
Browse files Browse the repository at this point in the history
  • Loading branch information
orizens committed Apr 24, 2017
1 parent a43e22f commit 784b9dd
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 151 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## v 0.5.0 (2017/04/24)
* [MAINTAINANCE] - code refactor - removing factories to rely more pure functional methods in PositionResolver, AxisResolver

## v 0.4.2 (2017/04/19)
* [NEW] - added **'[infiniteScroll]'** by [Angular's styleguide](https://angular.io/docs/ts/latest/guide/style-guide.html#!#02-06). **[infinite-scroll]** will be deprecated in future version.
* reduced bundle size with imports for RxJS Observable and Subscription objects #126.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngx-infinite-scroll",
"version": "0.4.3",
"version": "0.5.0",
"description": "An infinite scroll directive for Angular compatible with AoT compilation and Tree shaking",
"main": "./bundles/ngx-infinite-scroll.umd.js",
"module": "./modules/ngx-infinite-scroll.es5.js",
Expand Down
18 changes: 12 additions & 6 deletions src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,32 @@ export interface InfiniteScrollEvent {
currentScrollPosition: number;
};

export interface PositionElements {
export interface IPositionElements {
windowElement: ContainerRef;
horizontal: boolean;
axis: any;
}

export interface PositionStats {
export interface IPositionStats {
height: number;
scrolledUntilNow: number;
totalToScroll: number;
}

export interface ScrollerConfig {
export interface IScrollerConfig {
distance: {
down: number;
up: number;
};
scrollParent?: ContainerRef;
}

export interface ScrollStats {
export interface IScrollStats {
isScrollingDown: boolean;
shouldScroll: boolean
shouldScroll: boolean;
}

export interface IResolver {
container: ContainerRef;
isWindow: boolean;
axis: any;
}
36 changes: 18 additions & 18 deletions src/modules/infinite-scroll.directive.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { InfiniteScrollEvent, ScrollStats, PositionStats } from '../models';
import { InfiniteScrollEvent, IScrollStats, IPositionStats, IResolver } from '../models';
import {
Directive, ElementRef, Input, Output,
EventEmitter, OnDestroy, OnInit,
SimpleChanges, NgZone
} from '@angular/core';
import { PositionResolverFactory } from '../services/position-resolver';
import { ScrollRegister, ScrollRegisterConfig } from '../services/scroll-register';
import { PositionResolver } from '../services/position-resolver';
import { ScrollRegister, IScrollRegisterConfig } from '../services/scroll-register';
import { ScrollResolver } from '../services/scroll-resolver';
import { AxisResolver } from '../services/axis-resolver';

import { Subscription } from 'rxjs/Subscription';

@Directive({
Expand All @@ -31,37 +33,35 @@ export class InfiniteScrollDirective implements OnDestroy, OnInit {
constructor(
private element: ElementRef,
private zone: NgZone,
private positionResolverFactory: PositionResolverFactory,
private positionResolver: PositionResolver,
private scrollRegister: ScrollRegister,
private scrollerResolver: ScrollResolver
) {}

ngOnInit() {
if (typeof window !== 'undefined') {
const containerElement = this.resolveContainerElement();
const positionResolver = this.positionResolverFactory.create({
horizontal: this.horizontal,
windowElement: containerElement
const resolver = this.positionResolver.create({
axis: new AxisResolver(!this.horizontal),
windowElement: containerElement,
});
const options: ScrollRegisterConfig = {
container: positionResolver.container,
const options: IScrollRegisterConfig = {
container: resolver.container,
filterBefore: () => !this.infiniteScrollDisabled,
mergeMap: () => positionResolver.calculatePoints(this.element),
scrollHandler: (container: PositionStats) => this.handleOnScroll(container),
mergeMap: () => this.positionResolver.calculatePoints(this.element, resolver),
scrollHandler: (container: IPositionStats) => this.handleOnScroll(container),
throttleDuration: this.infiniteScrollThrottle
};
this.disposeScroller = this.scrollRegister.attachEvent(options);
}
}

handleOnScroll(container: PositionStats) {
const scrollResolverConfig = {
distance: {
down: this.infiniteScrollDistance,
up: this.infiniteScrollUpDistance
}
handleOnScroll(container: IPositionStats) {
const distance = {
down: this.infiniteScrollDistance,
up: this.infiniteScrollUpDistance
};
const scrollStats: ScrollStats = this.scrollerResolver.getScrollStats(container, scrollResolverConfig);
const scrollStats: IScrollStats = this.scrollerResolver.getScrollStats(container, { distance });
if (this.shouldTriggerEvents(scrollStats.shouldScroll)) {
const infiniteScrollEvent: InfiniteScrollEvent = {
currentScrollPosition: container.scrolledUntilNow
Expand Down
6 changes: 2 additions & 4 deletions src/modules/ngx-infinite-scroll.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { NgModule } from '@angular/core';

import { InfiniteScrollDirective } from './infinite-scroll.directive';
import { AxisResolverFactory } from '../services/axis-resolver';
import { PositionResolverFactory } from '../services/position-resolver';
import { PositionResolver } from '../services/position-resolver';
import { ScrollRegister } from '../services/scroll-register';
import { ScrollResolver } from '../services/scroll-resolver';

Expand All @@ -11,8 +10,7 @@ import { ScrollResolver } from '../services/scroll-resolver';
exports: [InfiniteScrollDirective],
imports: [],
providers: [
AxisResolverFactory,
PositionResolverFactory,
PositionResolver,
ScrollRegister,
ScrollResolver
]
Expand Down
6 changes: 3 additions & 3 deletions src/ngx-infinite-scroll.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Public classes.
export { InfiniteScrollDirective } from './modules/infinite-scroll.directive';
export { PositionResolver, PositionResolverFactory } from './services/position-resolver';
export { AxisResolver, AxisResolverFactory } from './services/axis-resolver';
export { PositionResolver } from './services/position-resolver';
export { AxisResolver } from './services/axis-resolver';
export { ScrollRegister } from './services/scroll-register';
export { ScrollResolver } from './services/scroll-resolver';
export { InfiniteScrollModule } from './modules/ngx-infinite-scroll.module';

export {
ContainerRef, InfiniteScrollEvent,
PositionElements, PositionStats, ScrollStats, ScrollerConfig
IPositionElements, IPositionStats, IScrollStats, IScrollerConfig, IResolver
} from './models';
11 changes: 0 additions & 11 deletions src/services/axis-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
import { Injectable, Inject } from '@angular/core';

@Injectable()
export class AxisResolverFactory {
constructor() { }

create(vertical: boolean = true) {
return new AxisResolver(vertical);
}
}

export class AxisResolver {
constructor(private vertical: boolean = true) {
}
Expand Down
119 changes: 55 additions & 64 deletions src/services/position-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,112 +1,103 @@
import { Injectable, ElementRef } from '@angular/core';
import { AxisResolver, AxisResolverFactory } from './axis-resolver';
import { ContainerRef, PositionElements, PositionStats } from '../models';
import { AxisResolver } from './axis-resolver';
import { ContainerRef, IPositionElements, IPositionStats, IResolver } from '../models';

@Injectable()
export class PositionResolverFactory {

constructor(private axisResolver: AxisResolverFactory) {
}

create (options: PositionElements) {
return new PositionResolver(this.axisResolver.create(!options.horizontal), options);
}
}

export class PositionResolver {
private documentElement: ContainerRef;
private isContainerWindow: boolean;
public container: ContainerRef;

constructor (private axis: AxisResolver, private options: PositionElements) {
this.resolveContainer(this.options.windowElement);
this.defineContainer(this.options.windowElement);
create(options: IPositionElements): IResolver {
const isWindow = this.isElementWindow(options.windowElement);
const resolver: IResolver = {
axis: options.axis,
container: this.defineContainer(options.windowElement, isWindow),
isWindow,
};
return resolver;
}

defineContainer(windowElement: ContainerRef) {
if (this.resolveContainer(windowElement) || !windowElement.nativeElement) {
this.container = windowElement;
} else {
this.container = windowElement.nativeElement;
}
return this.container;
defineContainer(windowElement: ContainerRef, isContainerWindow: boolean) {
const container = (isContainerWindow || !windowElement.nativeElement)
? windowElement
: windowElement.nativeElement;
return container;
}

resolveContainer(windowElement: ContainerRef): boolean {
const isContainerWindow = Object.prototype.toString.call(windowElement).includes('Window');
this.isContainerWindow = isContainerWindow;
return isContainerWindow;
isElementWindow(windowElement: ContainerRef): boolean {
const isWindow = Object.prototype.toString.call(windowElement).includes('Window');
return isWindow;
}

getDocumentElement() {
return this.isContainerWindow
? this.options.windowElement.document.documentElement
getDocumentElement(isContainerWindow: boolean, windowElement) {
return isContainerWindow
? windowElement.document.documentElement
: null;
}

calculatePoints (element: ElementRef) {
return this.isContainerWindow
? this.calculatePointsForWindow(element)
: this.calculatePointsForElement(element);
calculatePoints (element: ElementRef, resolver: IResolver) {
return resolver.isWindow
? this.calculatePointsForWindow(element, resolver)
: this.calculatePointsForElement(element, resolver);
}

calculatePointsForWindow (element: ElementRef): PositionStats {
calculatePointsForWindow (element: ElementRef, resolver: IResolver): IPositionStats {
const { axis, container, isWindow } = resolver;
const offsetHeightKey = axis.offsetHeightKey();
const clientHeightKey = axis.clientHeightKey();
const topKey = axis.topKey();
// container's height
const height = this.height(this.container);
const height = this.height(container, isWindow, offsetHeightKey, clientHeightKey);
// scrolled until now / current y point
const scrolledUntilNow = height + this.pageYOffset(this.getDocumentElement());
const scrolledUntilNow = height + this.pageYOffset(this.getDocumentElement(isWindow, container), axis, isWindow);
// total height / most bottom y point
const totalToScroll = this.offsetTop(element.nativeElement) + this.height(element.nativeElement);
const nativeElementHeight = this.height(element.nativeElement, isWindow, offsetHeightKey, clientHeightKey);
const totalToScroll = this.offsetTop(element.nativeElement, axis, isWindow) + nativeElementHeight;
return { height, scrolledUntilNow, totalToScroll };
}

calculatePointsForElement (element: ElementRef) {
let scrollTop = this.axis.scrollTopKey();
let scrollHeight = this.axis.scrollHeightKey();
const container = this.container;
calculatePointsForElement (element: ElementRef, resolver: IResolver) {
const { axis, container, isWindow } = resolver;
const offsetHeightKey = axis.offsetHeightKey();
const clientHeightKey = axis.clientHeightKey();
const scrollTop = axis.scrollTopKey();
const scrollHeight = axis.scrollHeightKey();
const topKey = axis.topKey();

const height = this.height(container);
const height = this.height(container, isWindow, offsetHeightKey, clientHeightKey);
// perhaps use this.container.offsetTop instead of 'scrollTop'
const scrolledUntilNow = container[scrollTop];
let containerTopOffset = 0;
const offsetTop = this.offsetTop(container);
const offsetTop = this.offsetTop(container, axis, isWindow);
if (offsetTop !== void 0) {
containerTopOffset = offsetTop;
}
const totalToScroll = container[scrollHeight];
return { height, scrolledUntilNow, totalToScroll };
}

private height (elem: any) {
let offsetHeight = this.axis.offsetHeightKey();
let clientHeight = this.axis.clientHeightKey();

// elem = elem.nativeElement;
if (isNaN(elem[offsetHeight])) {
return this.getDocumentElement()[clientHeight];
private height (elem: any, isWindow: boolean, offsetHeightKey: string, clientHeightKey: string) {
if (isNaN(elem[offsetHeightKey])) {
return this.getDocumentElement(isWindow, elem)[clientHeightKey];
} else {
return elem[offsetHeight];
return elem[offsetHeightKey];
}
}

private offsetTop (elem: any) {
let top = this.axis.topKey();

private offsetTop (elem: ContainerRef, axis: AxisResolver, isWindow: boolean) {
const topKey = axis.topKey();
// elem = elem.nativeElement;
if (!elem.getBoundingClientRect) { // || elem.css('none')) {
return;
}
return elem.getBoundingClientRect()[top] + this.pageYOffset(elem);
return elem.getBoundingClientRect()[topKey] + this.pageYOffset(elem, axis, isWindow);
}

pageYOffset (elem: any) {
let pageYOffset = this.axis.pageYOffsetKey();
let scrollTop = this.axis.scrollTopKey();
let offsetTop = this.axis.offsetTopKey();
private pageYOffset (elem: ContainerRef, axis: AxisResolver, isWindow: boolean) {
const pageYOffset = axis.pageYOffsetKey();
const scrollTop = axis.scrollTopKey();
const offsetTop = axis.offsetTopKey();

// elem = elem.nativeElement;
if (isNaN(window[pageYOffset])) {
return this.getDocumentElement()[scrollTop];
return this.getDocumentElement(isWindow, elem)[scrollTop];
} else if (elem.ownerDocument) {
return elem.ownerDocument.defaultView[pageYOffset];
} else {
Expand Down
7 changes: 4 additions & 3 deletions src/services/scroll-register.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ContainerRef } from '../models';
import { ContainerRef, IPositionStats, IScrollStats } from '../models';
import { Injectable, ElementRef } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';

import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/sampleTime';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/mergeMap';

export interface ScrollRegisterConfig {
export interface IScrollRegisterConfig {
container: ContainerRef;
throttleDuration: number;
filterBefore: () => boolean;
Expand All @@ -18,7 +19,7 @@ export interface ScrollRegisterConfig {

@Injectable()
export class ScrollRegister {
attachEvent (options: ScrollRegisterConfig): Subscription {
attachEvent (options: IScrollRegisterConfig): Subscription {
const scroller$: Subscription = Observable.fromEvent(options.container, 'scroll')
.sampleTime(options.throttleDuration)
.filter(options.filterBefore)
Expand Down
8 changes: 4 additions & 4 deletions src/services/scroll-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { PositionStats, ScrollerConfig } from '../models';
import { IPositionStats, IScrollerConfig } from '../models';
import { Injectable } from '@angular/core';

@Injectable()
export class ScrollResolver {
public lastScrollPosition: number = 0;

shouldScroll (container: PositionStats, config: ScrollerConfig, scrollingDown: boolean) {
shouldScroll (container: IPositionStats, config: IScrollerConfig, scrollingDown: boolean) {
const distance = config.distance;
let remaining: number;
let containerBreakpoint: number;
Expand All @@ -21,11 +21,11 @@ export class ScrollResolver {
return shouldScroll;
}

isScrollingDown (container: PositionStats) {
isScrollingDown (container: IPositionStats) {
return this.lastScrollPosition < container.scrolledUntilNow;
}

getScrollStats (container: PositionStats, config: ScrollerConfig) {
getScrollStats (container: IPositionStats, config: IScrollerConfig) {
const isScrollingDown = this.isScrollingDown(container);
const shouldScroll = this.shouldScroll(container, config, isScrollingDown);
return { isScrollingDown, shouldScroll };
Expand Down
Loading

0 comments on commit 784b9dd

Please sign in to comment.