UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

573 lines (565 loc) 23.1 kB
/** * @license * Copyright Google LLC 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 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/cdk/platform'), require('@angular/core'), require('rxjs'), require('rxjs/operators')) : typeof define === 'function' && define.amd ? define('@angular/cdk/scrolling', ['exports', '@angular/cdk/platform', '@angular/core', 'rxjs', 'rxjs/operators'], factory) : (factory((global.ng = global.ng || {}, global.ng.cdk = global.ng.cdk || {}, global.ng.cdk.scrolling = {}),global.ng.cdk.platform,global.ng.core,global.rxjs,global.rxjs.operators)); }(this, (function (exports,platform,core,rxjs,operators) { 'use strict'; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * Time in ms to throttle the scrolling events by default. */ var /** @type {?} */ 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. */ var ScrollDispatcher = /** @class */ (function () { function ScrollDispatcher(_ngZone, _platform) { this._ngZone = _ngZone; this._platform = _platform; /** * Subject for notifying that a registered scrollable reference element has been scrolled. */ this._scrolled = new rxjs.Subject(); /** * Keeps track of the global `scroll` and `resize` subscriptions. */ this._globalSubscription = null; /** * Keeps track of the amount of subscriptions to `scrolled`. Used for cleaning up afterwards. */ this._scrolledCount = 0; /** * Map of all the scrollable references that are registered with the service and their * scroll event subscriptions. */ this.scrollContainers = new Map(); } /** * Registers a scrollable instance with the service and listens for its scrolled events. When the * scrollable is scrolled, the service emits the event to its scrolled observable. * @param scrollable Scrollable instance to be registered. */ /** * Registers a scrollable instance with the service and listens for its scrolled events. When the * scrollable is scrolled, the service emits the event to its scrolled observable. * @param {?} scrollable Scrollable instance to be registered. * @return {?} */ ScrollDispatcher.prototype.register = /** * Registers a scrollable instance with the service and listens for its scrolled events. When the * scrollable is scrolled, the service emits the event to its scrolled observable. * @param {?} scrollable Scrollable instance to be registered. * @return {?} */ function (scrollable) { var _this = this; var /** @type {?} */ scrollSubscription = scrollable.elementScrolled() .subscribe(function () { return _this._scrolled.next(scrollable); }); this.scrollContainers.set(scrollable, scrollSubscription); }; /** * Deregisters a Scrollable reference and unsubscribes from its scroll event observable. * @param scrollable Scrollable instance to be deregistered. */ /** * Deregisters a Scrollable reference and unsubscribes from its scroll event observable. * @param {?} scrollable Scrollable instance to be deregistered. * @return {?} */ ScrollDispatcher.prototype.deregister = /** * Deregisters a Scrollable reference and unsubscribes from its scroll event observable. * @param {?} scrollable Scrollable instance to be deregistered. * @return {?} */ function (scrollable) { var /** @type {?} */ scrollableReference = this.scrollContainers.get(scrollable); if (scrollableReference) { scrollableReference.unsubscribe(); this.scrollContainers.delete(scrollable); } }; /** * Returns 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. * * **Note:** in order to avoid hitting change detection for every scroll event, * all of the events emitted from this stream will be run outside the Angular zone. * If you need to update any data bindings as a result of a scroll event, you have * to run the callback using `NgZone.run`. */ /** * Returns 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. * * **Note:** in order to avoid hitting change detection for every scroll event, * all of the events emitted from this stream will be run outside the Angular zone. * If you need to update any data bindings as a result of a scroll event, you have * to run the callback using `NgZone.run`. * @param {?=} auditTimeInMs * @return {?} */ ScrollDispatcher.prototype.scrolled = /** * Returns 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. * * **Note:** in order to avoid hitting change detection for every scroll event, * all of the events emitted from this stream will be run outside the Angular zone. * If you need to update any data bindings as a result of a scroll event, you have * to run the callback using `NgZone.run`. * @param {?=} auditTimeInMs * @return {?} */ function (auditTimeInMs) { var _this = this; if (auditTimeInMs === void 0) { auditTimeInMs = DEFAULT_SCROLL_TIME; } return this._platform.isBrowser ? rxjs.Observable.create(function (observer) { if (!_this._globalSubscription) { _this._addGlobalListener(); } // In the case of a 0ms delay, use an observable without auditTime // since it does add a perceptible delay in processing overhead. var /** @type {?} */ subscription = auditTimeInMs > 0 ? _this._scrolled.pipe(operators.auditTime(auditTimeInMs)).subscribe(observer) : _this._scrolled.subscribe(observer); _this._scrolledCount++; return function () { subscription.unsubscribe(); _this._scrolledCount--; if (!_this._scrolledCount) { _this._removeGlobalListener(); } }; }) : rxjs.of(); }; /** * @return {?} */ ScrollDispatcher.prototype.ngOnDestroy = /** * @return {?} */ function () { var _this = this; this._removeGlobalListener(); this.scrollContainers.forEach(function (_, container) { return _this.deregister(container); }); this._scrolled.complete(); }; /** * Returns an observable that emits whenever any of the * scrollable ancestors of an element are scrolled. * @param elementRef Element whose ancestors to listen for. * @param auditTimeInMs Time to throttle the scroll events. */ /** * Returns an observable that emits whenever any of the * scrollable ancestors of an element are scrolled. * @param {?} elementRef Element whose ancestors to listen for. * @param {?=} auditTimeInMs Time to throttle the scroll events. * @return {?} */ ScrollDispatcher.prototype.ancestorScrolled = /** * Returns an observable that emits whenever any of the * scrollable ancestors of an element are scrolled. * @param {?} elementRef Element whose ancestors to listen for. * @param {?=} auditTimeInMs Time to throttle the scroll events. * @return {?} */ function (elementRef, auditTimeInMs) { var /** @type {?} */ ancestors = this.getAncestorScrollContainers(elementRef); return this.scrolled(auditTimeInMs).pipe(operators.filter(function (target) { return !target || ancestors.indexOf(target) > -1; })); }; /** Returns all registered Scrollables that contain the provided element. */ /** * Returns all registered Scrollables that contain the provided element. * @param {?} elementRef * @return {?} */ ScrollDispatcher.prototype.getAncestorScrollContainers = /** * Returns all registered Scrollables that contain the provided element. * @param {?} elementRef * @return {?} */ function (elementRef) { var _this = this; var /** @type {?} */ scrollingContainers = []; this.scrollContainers.forEach(function (_subscription, scrollable) { if (_this._scrollableContainsElement(scrollable, elementRef)) { scrollingContainers.push(scrollable); } }); return scrollingContainers; }; /** * Returns true if the element is contained within the provided Scrollable. * @param {?} scrollable * @param {?} elementRef * @return {?} */ ScrollDispatcher.prototype._scrollableContainsElement = /** * Returns true if the element is contained within the provided Scrollable. * @param {?} scrollable * @param {?} elementRef * @return {?} */ function (scrollable, elementRef) { var /** @type {?} */ element = elementRef.nativeElement; var /** @type {?} */ 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 = /** @type {?} */ ((element)).parentElement); return false; }; /** * Sets up the global scroll listeners. * @return {?} */ ScrollDispatcher.prototype._addGlobalListener = /** * Sets up the global scroll listeners. * @return {?} */ function () { var _this = this; this._globalSubscription = this._ngZone.runOutsideAngular(function () { return rxjs.fromEvent(window.document, 'scroll').subscribe(function () { return _this._scrolled.next(); }); }); }; /** * Cleans up the global scroll listener. * @return {?} */ ScrollDispatcher.prototype._removeGlobalListener = /** * Cleans up the global scroll listener. * @return {?} */ function () { if (this._globalSubscription) { this._globalSubscription.unsubscribe(); this._globalSubscription = null; } }; ScrollDispatcher.decorators = [ { type: core.Injectable, args: [{ providedIn: 'root' },] }, ]; /** @nocollapse */ ScrollDispatcher.ctorParameters = function () { return [ { type: core.NgZone, }, { type: platform.Platform, }, ]; }; /** @nocollapse */ ScrollDispatcher.ngInjectableDef = core.defineInjectable({ factory: function ScrollDispatcher_Factory() { return new ScrollDispatcher(core.inject(core.NgZone), core.inject(platform.Platform)); }, token: ScrollDispatcher, providedIn: "root" }); return ScrollDispatcher; }()); /** * \@docs-private \@deprecated \@breaking-change 7.0.0 * @param {?} parentDispatcher * @param {?} ngZone * @param {?} platform * @return {?} */ function SCROLL_DISPATCHER_PROVIDER_FACTORY(parentDispatcher, ngZone, platform$$1) { return parentDispatcher || new ScrollDispatcher(ngZone, platform$$1); } /** * \@docs-private \@deprecated \@breaking-change 7.0.0 */ var /** @type {?} */ SCROLL_DISPATCHER_PROVIDER = { // If there is already a ScrollDispatcher available, use that. Otherwise, provide a new one. provide: ScrollDispatcher, deps: [[new core.Optional(), new core.SkipSelf(), ScrollDispatcher], core.NgZone, platform.Platform], useFactory: SCROLL_DISPATCHER_PROVIDER_FACTORY }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * 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. */ var CdkScrollable = /** @class */ (function () { function CdkScrollable(_elementRef, _scroll, _ngZone) { var _this = this; this._elementRef = _elementRef; this._scroll = _scroll; this._ngZone = _ngZone; this._elementScrolled = new rxjs.Subject(); this._scrollListener = function (event) { return _this._elementScrolled.next(event); }; } /** * @return {?} */ CdkScrollable.prototype.ngOnInit = /** * @return {?} */ function () { var _this = this; this._ngZone.runOutsideAngular(function () { _this.getElementRef().nativeElement.addEventListener('scroll', _this._scrollListener); }); this._scroll.register(this); }; /** * @return {?} */ CdkScrollable.prototype.ngOnDestroy = /** * @return {?} */ function () { this._scroll.deregister(this); if (this._scrollListener) { this.getElementRef().nativeElement.removeEventListener('scroll', this._scrollListener); } this._elementScrolled.complete(); }; /** * Returns observable that emits when a scroll event is fired on the host element. */ /** * Returns observable that emits when a scroll event is fired on the host element. * @return {?} */ CdkScrollable.prototype.elementScrolled = /** * Returns observable that emits when a scroll event is fired on the host element. * @return {?} */ function () { return this._elementScrolled.asObservable(); }; /** * @return {?} */ CdkScrollable.prototype.getElementRef = /** * @return {?} */ function () { return this._elementRef; }; CdkScrollable.decorators = [ { type: core.Directive, args: [{ selector: '[cdk-scrollable], [cdkScrollable]' },] }, ]; /** @nocollapse */ CdkScrollable.ctorParameters = function () { return [ { type: core.ElementRef, }, { type: ScrollDispatcher, }, { type: core.NgZone, }, ]; }; return CdkScrollable; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * Time in ms to throttle the resize events by default. */ var /** @type {?} */ DEFAULT_RESIZE_TIME = 20; /** * Simple utility for getting the bounds of the browser viewport. * \@docs-private */ var ViewportRuler = /** @class */ (function () { function ViewportRuler(_platform, ngZone) { var _this = this; this._platform = _platform; this._change = _platform.isBrowser ? ngZone.runOutsideAngular(function () { return rxjs.merge(rxjs.fromEvent(window, 'resize'), rxjs.fromEvent(window, 'orientationchange')); }) : rxjs.of(); this._invalidateCache = this.change().subscribe(function () { return _this._updateViewportSize(); }); } /** * @return {?} */ ViewportRuler.prototype.ngOnDestroy = /** * @return {?} */ function () { this._invalidateCache.unsubscribe(); }; /** Returns the viewport's width and height. */ /** * Returns the viewport's width and height. * @return {?} */ ViewportRuler.prototype.getViewportSize = /** * Returns the viewport's width and height. * @return {?} */ function () { if (!this._viewportSize) { this._updateViewportSize(); } var /** @type {?} */ output = { width: this._viewportSize.width, height: this._viewportSize.height }; // If we're not on a browser, don't cache the size since it'll be mocked out anyway. if (!this._platform.isBrowser) { this._viewportSize = /** @type {?} */ ((null)); } return output; }; /** Gets a ClientRect for the viewport's bounds. */ /** * Gets a ClientRect for the viewport's bounds. * @return {?} */ ViewportRuler.prototype.getViewportRect = /** * Gets a ClientRect for the viewport's bounds. * @return {?} */ function () { // Use the document element's bounding rect rather than the window scroll properties // (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE where window scroll // properties and client coordinates (boundingClientRect, clientX/Y, etc.) are in different // conceptual viewports. Under most circumstances these viewports are equivalent, but they // can disagree when the page is pinch-zoomed (on devices that support touch). // See https://bugs.chromium.org/p/chromium/issues/detail?id=489206#c4 // We use the documentElement instead of the body because, by default (without a css reset) // browsers typically give the document body an 8px margin, which is not included in // getBoundingClientRect(). var /** @type {?} */ scrollPosition = this.getViewportScrollPosition(); var _a = this.getViewportSize(), width = _a.width, height = _a.height; return { top: scrollPosition.top, left: scrollPosition.left, bottom: scrollPosition.top + height, right: scrollPosition.left + width, height: height, width: width, }; }; /** Gets the (top, left) scroll position of the viewport. */ /** * Gets the (top, left) scroll position of the viewport. * @return {?} */ ViewportRuler.prototype.getViewportScrollPosition = /** * Gets the (top, left) scroll position of the viewport. * @return {?} */ function () { // While we can get a reference to the fake document // during SSR, it doesn't have getBoundingClientRect. if (!this._platform.isBrowser) { return { top: 0, left: 0 }; } // The top-left-corner of the viewport is determined by the scroll position of the document // body, normally just (scrollLeft, scrollTop). However, Chrome and Firefox disagree about // whether `document.body` or `document.documentElement` is the scrolled element, so reading // `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding rect of // `document.documentElement` works consistently, where the `top` and `left` values will // equal negative the scroll position. var /** @type {?} */ documentRect = document.documentElement.getBoundingClientRect(); var /** @type {?} */ top = -documentRect.top || document.body.scrollTop || window.scrollY || document.documentElement.scrollTop || 0; var /** @type {?} */ left = -documentRect.left || document.body.scrollLeft || window.scrollX || document.documentElement.scrollLeft || 0; return { top: top, left: left }; }; /** * Returns a stream that emits whenever the size of the viewport changes. * @param throttleTime Time in milliseconds to throttle the stream. */ /** * Returns a stream that emits whenever the size of the viewport changes. * @param {?=} throttleTime Time in milliseconds to throttle the stream. * @return {?} */ ViewportRuler.prototype.change = /** * Returns a stream that emits whenever the size of the viewport changes. * @param {?=} throttleTime Time in milliseconds to throttle the stream. * @return {?} */ function (throttleTime) { if (throttleTime === void 0) { throttleTime = DEFAULT_RESIZE_TIME; } return throttleTime > 0 ? this._change.pipe(operators.auditTime(throttleTime)) : this._change; }; /** * Updates the cached viewport size. * @return {?} */ ViewportRuler.prototype._updateViewportSize = /** * Updates the cached viewport size. * @return {?} */ function () { this._viewportSize = this._platform.isBrowser ? { width: window.innerWidth, height: window.innerHeight } : { width: 0, height: 0 }; }; ViewportRuler.decorators = [ { type: core.Injectable, args: [{ providedIn: 'root' },] }, ]; /** @nocollapse */ ViewportRuler.ctorParameters = function () { return [ { type: platform.Platform, }, { type: core.NgZone, }, ]; }; /** @nocollapse */ ViewportRuler.ngInjectableDef = core.defineInjectable({ factory: function ViewportRuler_Factory() { return new ViewportRuler(core.inject(platform.Platform), core.inject(core.NgZone)); }, token: ViewportRuler, providedIn: "root" }); return ViewportRuler; }()); /** * \@docs-private \@deprecated \@breaking-change 7.0.0 * @param {?} parentRuler * @param {?} platform * @param {?} ngZone * @return {?} */ function VIEWPORT_RULER_PROVIDER_FACTORY(parentRuler, platform$$1, ngZone) { return parentRuler || new ViewportRuler(platform$$1, ngZone); } /** * \@docs-private \@deprecated \@breaking-change 7.0.0 */ var /** @type {?} */ VIEWPORT_RULER_PROVIDER = { // If there is already a ViewportRuler available, use that. Otherwise, provide a new one. provide: ViewportRuler, deps: [[new core.Optional(), new core.SkipSelf(), ViewportRuler], platform.Platform, core.NgZone], useFactory: VIEWPORT_RULER_PROVIDER_FACTORY }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ var ScrollDispatchModule = /** @class */ (function () { function ScrollDispatchModule() { } ScrollDispatchModule.decorators = [ { type: core.NgModule, args: [{ imports: [platform.PlatformModule], exports: [CdkScrollable], declarations: [CdkScrollable], },] }, ]; return ScrollDispatchModule; }()); exports.DEFAULT_SCROLL_TIME = DEFAULT_SCROLL_TIME; exports.ScrollDispatcher = ScrollDispatcher; exports.SCROLL_DISPATCHER_PROVIDER_FACTORY = SCROLL_DISPATCHER_PROVIDER_FACTORY; exports.SCROLL_DISPATCHER_PROVIDER = SCROLL_DISPATCHER_PROVIDER; exports.CdkScrollable = CdkScrollable; exports.DEFAULT_RESIZE_TIME = DEFAULT_RESIZE_TIME; exports.ViewportRuler = ViewportRuler; exports.VIEWPORT_RULER_PROVIDER_FACTORY = VIEWPORT_RULER_PROVIDER_FACTORY; exports.VIEWPORT_RULER_PROVIDER = VIEWPORT_RULER_PROVIDER; exports.ScrollDispatchModule = ScrollDispatchModule; Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=cdk-scrolling.umd.js.map