diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index cce0279..0000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -package.json -package-lock.json diff --git a/README.md b/README.md index 2a3f6a7..a38f24c 100644 --- a/README.md +++ b/README.md @@ -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 +

+ Amet tempor excepteur occaecat nulla. +

+``` + ## 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. diff --git a/e2e/src/app.e2e-spec.ts b/e2e/src/app.e2e-spec.ts index a688125..656f239 100644 --- a/e2e/src/app.e2e-spec.ts +++ b/e2e/src/app.e2e-spec.ts @@ -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' + ); + }); }); diff --git a/e2e/src/app.po.ts b/e2e/src/app.po.ts index 1fcd47b..a38cb32 100644 --- a/e2e/src/app.po.ts +++ b/e2e/src/app.po.ts @@ -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')); } diff --git a/src/app/app.component.html b/src/app/app.component.html index 029be3e..fea2b1e 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -26,4 +26,11 @@

Scroll down ↓ and right →

+

+ 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. +

+
diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 49f2bc6..25e391b 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -2,6 +2,10 @@ font-family: Roboto, sans-serif; } +p { + margin: 0; +} + .spacer { height: 100vh; width: 200vw; @@ -16,6 +20,13 @@ min-height: 200vh; } +.element--has-options { + &.sn-viewport--in { + background-color: pink; + color: black; + } +} + .scrollable { height: 200px; overflow: auto; diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 6c2977b..098b2b5 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -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; diff --git a/src/app/in-viewport/in-viewport.directive.spec.ts b/src/app/in-viewport/in-viewport.directive.spec.ts index dbd79cf..84ced2c 100644 --- a/src/app/in-viewport/in-viewport.directive.spec.ts +++ b/src/app/in-viewport/in-viewport.directive.spec.ts @@ -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; diff --git a/src/app/in-viewport/in-viewport.directive.ts b/src/app/in-viewport/in-viewport.directive.ts index 3063d6e..08ae062 100644 --- a/src/app/in-viewport/in-viewport.directive.ts +++ b/src/app/in-viewport/in-viewport.directive.ts @@ -6,7 +6,8 @@ import { Output, OnDestroy, AfterViewInit, - Inject + Inject, + Input } from '@angular/core'; import { WINDOW } from '../window/window-token'; @@ -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(); observer: IntersectionObserver; @@ -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);