aboutsummaryrefslogtreecommitdiffstats
path: root/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll
diff options
context:
space:
mode:
Diffstat (limited to 'deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll')
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/block-scroll-strategy.ts77
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/close-scroll-strategy.ts54
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/index.ts35
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/noop-scroll-strategy.ts24
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/reposition-scroll-strategy.ts59
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-dispatcher.ts174
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-strategy-options.ts52
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-strategy.ts29
-rw-r--r--deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scrollable.ts63
9 files changed, 567 insertions, 0 deletions
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/block-scroll-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/block-scroll-strategy.ts
new file mode 100644
index 00000000..d1c1d401
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/block-scroll-strategy.ts
@@ -0,0 +1,77 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector*/
+import {ViewportRuler} from '../position/viewport-ruler';
+
+import {ScrollStrategy} from './scroll-strategy';
+
+/**
+ * Strategy that will prevent the user from scrolling while the overlay is
+ * visible.
+ */
+export class BlockScrollStrategy implements ScrollStrategy {
+ private _previousHTMLStyles = {top: '', left: ''};
+ private _previousScrollPosition: {top: number, left: number};
+ private _isEnabled = false;
+
+ constructor(private _viewportRuler: ViewportRuler) {}
+
+ attach() {
+ //
+ }
+
+ enable() {
+ if (this._canBeEnabled()) {
+ const root = document.documentElement;
+
+ this._previousScrollPosition =
+ this._viewportRuler.getViewportScrollPosition();
+
+ // Cache the previous inline styles in case the user had set them.
+ this._previousHTMLStyles.left = root.style.left || '';
+ this._previousHTMLStyles.top = root.style.top || '';
+
+ // Note: we're using the `html` node, instead of the `body`, because the
+ // `body` may have the user agent margin, whereas the `html` is guaranteed
+ // not to have one.
+ root.style.left = `${- this._previousScrollPosition.left}px`;
+ root.style.top = `${- this._previousScrollPosition.top}px`;
+ root.classList.add('cdk-global-scrollblock');
+ this._isEnabled = true;
+ }
+ }
+
+ disable() {
+ if (this._isEnabled) {
+ this._isEnabled = false;
+ document.documentElement.style.left = this._previousHTMLStyles.left;
+ document.documentElement.style.top = this._previousHTMLStyles.top;
+ document.documentElement.classList.remove('cdk-global-scrollblock');
+ window.scroll(
+ this._previousScrollPosition.left, this._previousScrollPosition.top);
+ }
+ }
+
+ private _canBeEnabled(): boolean {
+ // Since the scroll strategies can't be singletons, we have to use a global
+ // CSS class
+ // (`cdk-global-scrollblock`) to make sure that we don't try to disable
+ // global scrolling multiple times.
+ if (document.documentElement.classList.contains('cdk-global-scrollblock') ||
+ this._isEnabled) {
+ return false;
+ }
+
+ const body = document.body;
+ const viewport = this._viewportRuler.getViewportRect();
+ return body.scrollHeight > viewport.height ||
+ body.scrollWidth > viewport.width;
+ }
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/close-scroll-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/close-scroll-strategy.ts
new file mode 100644
index 00000000..51189dc1
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/close-scroll-strategy.ts
@@ -0,0 +1,54 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector*/
+import {Subscription} from 'rxjs/Subscription';
+
+import {OverlayRef} from '../overlay-ref';
+
+import {ScrollDispatcher} from './scroll-dispatcher';
+import {getMdScrollStrategyAlreadyAttachedError, ScrollStrategy} from './scroll-strategy';
+
+
+/**
+ * Strategy that will close the overlay as soon as the user starts scrolling.
+ */
+export class CloseScrollStrategy implements ScrollStrategy {
+ private _scrollSubscription: Subscription|null = null;
+ private _overlayRef: OverlayRef;
+
+ constructor(private _scrollDispatcher: ScrollDispatcher) {}
+
+ attach(overlayRef: OverlayRef) {
+ if (this._overlayRef) {
+ throw getMdScrollStrategyAlreadyAttachedError();
+ }
+
+ this._overlayRef = overlayRef;
+ }
+
+ enable() {
+ if (!this._scrollSubscription) {
+ this._scrollSubscription = this._scrollDispatcher.scrolled(0, () => {
+ if (this._overlayRef.hasAttached()) {
+ this._overlayRef.detach();
+ }
+
+ this.disable();
+ });
+ }
+ }
+
+ disable() {
+ if (this._scrollSubscription) {
+ this._scrollSubscription.unsubscribe();
+ this._scrollSubscription = null;
+ }
+ }
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/index.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/index.ts
new file mode 100644
index 00000000..e386770b
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/index.ts
@@ -0,0 +1,35 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector*/
+import {PlatformModule} from '@angular/cdk';
+import {NgModule} from '@angular/core';
+
+import {SCROLL_DISPATCHER_PROVIDER} from './scroll-dispatcher';
+import {ScrollStrategyOptions} from './scroll-strategy-options';
+import {Scrollable} from './scrollable';
+
+export {BlockScrollStrategy} from './block-scroll-strategy';
+export {CloseScrollStrategy} from './close-scroll-strategy';
+export {NoopScrollStrategy} from './noop-scroll-strategy';
+export {RepositionScrollStrategy} from './reposition-scroll-strategy';
+export {ScrollDispatcher} from './scroll-dispatcher';
+// Export pre-defined scroll strategies and interface to build custom ones.
+export {ScrollStrategy} from './scroll-strategy';
+export {ScrollStrategyOptions} from './scroll-strategy-options';
+export {Scrollable} from './scrollable';
+
+@NgModule({
+ imports: [PlatformModule],
+ exports: [Scrollable],
+ declarations: [Scrollable],
+ providers: [SCROLL_DISPATCHER_PROVIDER, ScrollStrategyOptions],
+})
+export class ScrollDispatchModule {
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/noop-scroll-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/noop-scroll-strategy.ts
new file mode 100644
index 00000000..9b92ab49
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/noop-scroll-strategy.ts
@@ -0,0 +1,24 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {ScrollStrategy} from './scroll-strategy';
+
+/**
+ * Scroll strategy that doesn't do anything.
+ */
+export class NoopScrollStrategy implements ScrollStrategy {
+ enable() {
+ //
+ }
+ disable() {
+ //
+ }
+ attach() {
+ //
+ }
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/reposition-scroll-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/reposition-scroll-strategy.ts
new file mode 100644
index 00000000..b15d5dea
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/reposition-scroll-strategy.ts
@@ -0,0 +1,59 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector*/
+import {Subscription} from 'rxjs/Subscription';
+
+import {OverlayRef} from '../overlay-ref';
+
+import {ScrollDispatcher} from './scroll-dispatcher';
+import {getMdScrollStrategyAlreadyAttachedError, ScrollStrategy} from './scroll-strategy';
+
+/**
+ * Config options for the RepositionScrollStrategy.
+ */
+export interface RepositionScrollStrategyConfig { scrollThrottle?: number; }
+
+/**
+ * Strategy that will update the element position as the user is scrolling.
+ */
+export class RepositionScrollStrategy implements ScrollStrategy {
+ private _scrollSubscription: Subscription|null = null;
+ private _overlayRef: OverlayRef;
+
+ constructor(
+ private _scrollDispatcher: ScrollDispatcher,
+ private _config?: RepositionScrollStrategyConfig) {}
+
+ attach(overlayRef: OverlayRef) {
+ if (this._overlayRef) {
+ throw getMdScrollStrategyAlreadyAttachedError();
+ }
+
+ this._overlayRef = overlayRef;
+ }
+
+ enable() {
+ if (!this._scrollSubscription) {
+ const throttle = this._config ? this._config.scrollThrottle : 0;
+
+ this._scrollSubscription =
+ this._scrollDispatcher.scrolled(throttle, () => {
+ this._overlayRef.updatePosition();
+ });
+ }
+ }
+
+ disable() {
+ if (this._scrollSubscription) {
+ this._scrollSubscription.unsubscribe();
+ this._scrollSubscription = null;
+ }
+ }
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-dispatcher.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-dispatcher.ts
new file mode 100644
index 00000000..2c145af5
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-dispatcher.ts
@@ -0,0 +1,174 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector*/
+import {Platform} from '@angular/cdk';
+import {auditTime} from 'rxjs/operator/auditTime';
+import {ElementRef, Injectable, NgZone, Optional, SkipSelf} from '@angular/core';
+import {fromEvent} from 'rxjs/observable/fromEvent';
+import {merge} from 'rxjs/observable/merge';
+import {Subject} from 'rxjs/Subject';
+import {Subscription} from 'rxjs/Subscription';
+
+import {Scrollable} from './scrollable';
+
+
+/** Time in ms to throttle the scrolling events by default. */
+export const DEFAULT_SCROLL_TIME = 20;
+
+/**
+ * Service contained all registered Scrollable references and emits an event
+ * when any one of the Scrollable references emit a scrolled event.
+ */
+@Injectable()
+export class ScrollDispatcher {
+ /** Subject for notifying that a registered scrollable reference element has
+ * been scrolled. */
+ _scrolled: Subject<void> = new Subject<void>();
+
+ /** Keeps track of the global `scroll` and `resize` subscriptions. */
+ _globalSubscription: Subscription|null = null;
+
+ /** Keeps track of the amount of subscriptions to `scrolled`. Used for
+ * cleaning up afterwards. */
+ private _scrolledCount = 0;
+
+ /**
+ * Map of all the scrollable references that are registered with the service
+ * and their scroll event subscriptions.
+ */
+ scrollableReferences: Map<Scrollable, Subscription> = new Map();
+
+ constructor(private _ngZone: NgZone, private _platform: Platform) {}
+
+ /**
+ * Registers a Scrollable with the service and listens for its scrolled
+ * events. When the scrollable is scrolled, the service emits the event in its
+ * scrolled observable.
+ * @param scrollable Scrollable instance to be registered.
+ */
+ register(scrollable: Scrollable): void {
+ const scrollSubscription =
+ scrollable.elementScrolled().subscribe(() => this._notify());
+
+ this.scrollableReferences.set(scrollable, scrollSubscription);
+ }
+
+ /**
+ * Deregisters a Scrollable reference and unsubscribes from its scroll event
+ * observable.
+ * @param scrollable Scrollable instance to be deregistered.
+ */
+ deregister(scrollable: Scrollable): void {
+ const scrollableReference = this.scrollableReferences.get(scrollable);
+
+ if (scrollableReference) {
+ scrollableReference.unsubscribe();
+ this.scrollableReferences.delete(scrollable);
+ }
+ }
+
+ /**
+ * Subscribes to an observable that emits an event whenever any of the
+ * registered Scrollable references (or window, document, or body) fire a
+ * scrolled event. Can provide a time in ms to override the default "throttle"
+ * time.
+ */
+ scrolled(auditTimeInMs: number = DEFAULT_SCROLL_TIME, callback: () => any):
+ Subscription {
+ // Scroll events can only happen on the browser, so do nothing if we're not
+ // on the browser.
+ if (!this._platform.isBrowser) {
+ return Subscription.EMPTY;
+ }
+
+ // In the case of a 0ms delay, use an observable without auditTime
+ // since it does add a perceptible delay in processing overhead.
+ const observable = auditTimeInMs > 0 ?
+ auditTime.call(this._scrolled.asObservable(), auditTimeInMs) :
+ this._scrolled.asObservable();
+
+ this._scrolledCount++;
+
+ if (!this._globalSubscription) {
+ this._globalSubscription = this._ngZone.runOutsideAngular(() => {
+ return merge(
+ fromEvent(window.document, 'scroll'),
+ fromEvent(window, 'resize'))
+ .subscribe(() => this._notify());
+ });
+ }
+
+ // Note that we need to do the subscribing from here, in order to be able to
+ // remove the global event listeners once there are no more subscriptions.
+ const subscription = observable.subscribe(callback);
+
+ subscription.add(() => {
+ this._scrolledCount--;
+
+ if (this._globalSubscription && !this.scrollableReferences.size &&
+ !this._scrolledCount) {
+ this._globalSubscription.unsubscribe();
+ this._globalSubscription = null;
+ }
+ });
+
+ return subscription;
+ }
+
+ /** Returns all registered Scrollables that contain the provided element. */
+ getScrollContainers(elementRef: ElementRef): Scrollable[] {
+ const scrollingContainers: Scrollable[] = [];
+
+ this.scrollableReferences.forEach(
+ (_subscription: Subscription, scrollable: Scrollable) => {
+ if (this.scrollableContainsElement(scrollable, elementRef)) {
+ scrollingContainers.push(scrollable);
+ }
+ });
+
+ return scrollingContainers;
+ }
+
+ /** Returns true if the element is contained within the provided Scrollable.
+ */
+ scrollableContainsElement(scrollable: Scrollable, elementRef: ElementRef):
+ boolean {
+ let element = elementRef.nativeElement;
+ const scrollableElement = scrollable.getElementRef().nativeElement;
+
+ // Traverse through the element parents until we reach null, checking if any
+ // of the elements are the scrollable's element.
+ do {
+ if (element === scrollableElement) {
+ return true;
+ }
+ } while (element = element.parentElement);
+
+ return false;
+ }
+
+ /** Sends a notification that a scroll event has been fired. */
+ _notify() {
+ this._scrolled.next();
+ }
+}
+
+export function SCROLL_DISPATCHER_PROVIDER_FACTORY(
+ parentDispatcher: ScrollDispatcher, ngZone: NgZone, platform: Platform) {
+ return parentDispatcher || new ScrollDispatcher(ngZone, platform);
+}
+
+export const SCROLL_DISPATCHER_PROVIDER = {
+ // If there is already a ScrollDispatcher available, use that. Otherwise,
+ // provide a new one.
+ provide: ScrollDispatcher,
+ deps: [[new Optional(), new SkipSelf(), ScrollDispatcher], NgZone, Platform],
+ useFactory: SCROLL_DISPATCHER_PROVIDER_FACTORY
+};
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-strategy-options.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-strategy-options.ts
new file mode 100644
index 00000000..f6270388
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-strategy-options.ts
@@ -0,0 +1,52 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector no-unused-expression*/
+import {Injectable} from '@angular/core';
+
+import {ViewportRuler} from '../position/viewport-ruler';
+
+import {BlockScrollStrategy} from './block-scroll-strategy';
+import {CloseScrollStrategy} from './close-scroll-strategy';
+import {NoopScrollStrategy} from './noop-scroll-strategy';
+import {RepositionScrollStrategy, RepositionScrollStrategyConfig} from './reposition-scroll-strategy';
+import {ScrollDispatcher} from './scroll-dispatcher';
+// import {ScrollStrategy} from './scroll-strategy';
+
+
+/**
+ * Options for how an overlay will handle scrolling.
+ *
+ * Users can provide a custom value for `ScrollStrategyOptions` to replace the
+ * default behaviors. This class primarily acts as a factory for ScrollStrategy
+ * instances.
+ */
+@Injectable()
+export class ScrollStrategyOptions {
+ constructor(
+ private _scrollDispatcher: ScrollDispatcher,
+ private _viewportRuler: ViewportRuler) {}
+
+ /** Do nothing on scroll. */
+ noop = () => new NoopScrollStrategy();
+
+ /** Close the overlay as soon as the user scrolls. */
+ close = () => new CloseScrollStrategy(this._scrollDispatcher);
+
+ /** Block scrolling. */
+ block = () => new BlockScrollStrategy(this._viewportRuler);
+
+ /**
+ * Update the overlay's position on scroll.
+ * @param config Configuration to be used inside the scroll strategy.
+ * Allows debouncing the reposition calls.
+ */
+ reposition = (config?: RepositionScrollStrategyConfig) =>
+ new RepositionScrollStrategy(this._scrollDispatcher, config)
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-strategy.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-strategy.ts
new file mode 100644
index 00000000..d59651a7
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scroll-strategy.ts
@@ -0,0 +1,29 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector*/
+import {OverlayRef} from '../overlay-ref';
+
+/**
+ * Describes a strategy that will be used by an overlay
+ * to handle scroll events while it is open.
+ */
+export abstract class ScrollStrategy {
+ enable: () => void;
+ disable: () => void;
+ attach: (overlayRef: OverlayRef) => void;
+}
+
+/**
+ * Returns an error to be thrown when attempting to attach an already-attached
+ * scroll strategy.
+ */
+export function getMdScrollStrategyAlreadyAttachedError(): Error {
+ return Error(`Scroll strategy has already been attached.`);
+}
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scrollable.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scrollable.ts
new file mode 100644
index 00000000..fe7b041c
--- /dev/null
+++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/paletx/core/overlay/scroll/scrollable.ts
@@ -0,0 +1,63 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+/* tslint:disable:array-type member-access variable-name typedef
+ only-arrow-functions directive-class-suffix component-class-suffix
+ component-selector*/
+import {Directive, ElementRef, NgZone, OnDestroy, OnInit, Renderer2} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Subject} from 'rxjs/Subject';
+
+import {ScrollDispatcher} from './scroll-dispatcher';
+
+
+/**
+ * Sends an event when the directive's element is scrolled. Registers itself
+ * with the ScrollDispatcher service to include itself as part of its collection
+ * of scrolling events that it can be listened to through the service.
+ */
+@Directive({selector: '[cdk-scrollable], [cdkScrollable]'})
+export class Scrollable implements OnInit, OnDestroy {
+ private _elementScrolled: Subject<Event> = new Subject();
+ private _scrollListener: Function|null;
+
+ constructor(
+ private _elementRef: ElementRef, private _scroll: ScrollDispatcher,
+ private _ngZone: NgZone, private _renderer: Renderer2) {}
+
+ ngOnInit() {
+ this._scrollListener = this._ngZone.runOutsideAngular(() => {
+ return this._renderer.listen(
+ this.getElementRef().nativeElement, 'scroll', (event: Event) => {
+ this._elementScrolled.next(event);
+ });
+ });
+
+ this._scroll.register(this);
+ }
+
+ ngOnDestroy() {
+ this._scroll.deregister(this);
+
+ if (this._scrollListener) {
+ this._scrollListener();
+ this._scrollListener = null;
+ }
+ }
+
+ /**
+ * Returns observable that emits when a scroll event is fired on the host
+ * element.
+ */
+ elementScrolled(): Observable<any> {
+ return this._elementScrolled.asObservable();
+ }
+
+ getElementRef(): ElementRef {
+ return this._elementRef;
+ }
+}