Skip to content

Commit

Permalink
feat(InViewport): add ability set offset using IntersectionObserver o…
Browse files Browse the repository at this point in the history
…ptions (#26)

Add a `inViewportOptions` property that maps to the IntersectionObserver options parameter. This allows offsets to be set when determining the `inViewport` status.

Fix #4
  • Loading branch information
edoparearyee committed Oct 8, 2018
1 parent 61bb179 commit cacca75
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 4 deletions.
1 change: 1 addition & 0 deletions .npmrc
@@ -0,0 +1 @@
package-lock=false
2 changes: 0 additions & 2 deletions .prettierignore

This file was deleted.

17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -166,6 +166,23 @@ export class AppComponent {
}
```

### Offset example

You can pass any options [Intersection Observer][intersection-observer-api] accepts using the `[inViewportOptions]` property. This allows offsets to be set using the `rootMargin` property. This property works the same as `margin` property in CSS.

#### `app.component.html`

```html
<p
class="foo"
snInViewport
[inViewportOptions]="{
rootMargin: '100px 0px 0px 0px'
}">
Amet tempor excepteur occaecat nulla.
</p>
```

## Development server

Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
Expand Down
17 changes: 17 additions & 0 deletions e2e/src/app.e2e-spec.ts
Expand Up @@ -96,4 +96,21 @@ describe('InViewport Lib E2E Tests', function() {
'sn-viewport--out'
);
});

it('should apply offset to element with options', () => {
page.scrollTo(0, 768 * 2.4);
expect(page.getElementWithOptions().getAttribute('class')).toContain(
'sn-viewport--out'
);

page.scrollTo(0, 768 * 2.5);
expect(page.getElementWithOptions().getAttribute('class')).toContain(
'sn-viewport--in'
);

page.scrollTo(0, 768 * 3.6);
expect(page.getElementWithOptions().getAttribute('class')).not.toContain(
'sn-viewport--in'
);
});
});
4 changes: 4 additions & 0 deletions e2e/src/app.po.ts
Expand Up @@ -13,6 +13,10 @@ export class AppPage {
return element(by.css('.element--large'));
}

getElementWithOptions() {
return element(by.css('.element--has-options'));
}

getScrollableInnerElement() {
return element(by.css('.scrollable__inner'));
}
Expand Down
7 changes: 7 additions & 0 deletions src/app/app.component.html
Expand Up @@ -26,4 +26,11 @@ <h1>Scroll down ↓ and right →</h1>

</div>

<p
class="element--has-options"
snInViewport
[inViewportOptions]="options">
Amet tempor excepteur occaecat nulla. Aute deserunt esse duis velit mollit exercitation nisi officia. Anim do irure cillum esse esse ea magna elit. Velit reprehenderit eiusmod aliqua do reprehenderit elit commodo enim do deserunt consequat ea elit. Incididunt dolore officia consectetur anim ex ea commodo ullamco veniam ipsum esse amet cupidatat commodo. Id consequat duis aliqua minim aliquip enim officia elit. Sint est fugiat ex ex proident ad veniam do excepteur.
</p>

<div class="spacer"></div>
11 changes: 11 additions & 0 deletions src/app/app.component.scss
Expand Up @@ -2,6 +2,10 @@
font-family: Roboto, sans-serif;
}

p {
margin: 0;
}

.spacer {
height: 100vh;
width: 200vw;
Expand All @@ -16,6 +20,13 @@
min-height: 200vh;
}

.element--has-options {
&.sn-viewport--in {
background-color: pink;
color: black;
}
}

.scrollable {
height: 200px;
overflow: auto;
Expand Down
3 changes: 3 additions & 0 deletions src/app/app.component.ts
Expand Up @@ -7,6 +7,9 @@ import { Component } from '@angular/core';
})
export class AppComponent {
highlight = false;
options: IntersectionObserverInit = {
rootMargin: '-100px 0px 50px 0px'
};

onInViewportChange(inViewport: boolean) {
this.highlight = inViewport;
Expand Down
15 changes: 15 additions & 0 deletions src/app/in-viewport/in-viewport.directive.spec.ts
Expand Up @@ -49,6 +49,21 @@ describe('InViewportDirective', () => {
expect(spy).toHaveBeenCalledWith(true);
});

it('should add options to IntersectionObserver', () => {
const spy = jasmine.createSpy('spy').and.returnValue({
observe: () => null,
unobserve: () => null
});
WINDOW_MOCK.IntersectionObserver = spy;
directive.inViewportOptions = { rootMargin: '100px' };
directive.ngAfterViewInit();
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(
jasmine.any(Function),
directive.inViewportOptions
);
});

it('should should unobserve on destroy', () => {
const spy = jasmine.createSpy('spy');
directive.observer.unobserve = spy;
Expand Down
8 changes: 6 additions & 2 deletions src/app/in-viewport/in-viewport.directive.ts
Expand Up @@ -6,7 +6,8 @@ import {
Output,
OnDestroy,
AfterViewInit,
Inject
Inject,
Input
} from '@angular/core';
import { WINDOW } from '../window/window-token';

Expand All @@ -32,6 +33,8 @@ import { WINDOW } from '../window/window-token';
})
export class InViewportDirective implements AfterViewInit, OnDestroy {
private inViewport: boolean;
@Input()
inViewportOptions: IntersectionObserverInit;
@Output()
inViewportChange = new EventEmitter<boolean>();
observer: IntersectionObserver;
Expand All @@ -51,7 +54,8 @@ export class InViewportDirective implements AfterViewInit, OnDestroy {
ngAfterViewInit() {
const IntersectionObserver = this.window['IntersectionObserver'];
this.observer = new IntersectionObserver(
this.intersectionObserverCallback.bind(this)
this.intersectionObserverCallback.bind(this),
this.inViewportOptions
);

this.observer.observe(this.el.nativeElement);
Expand Down

0 comments on commit cacca75

Please sign in to comment.