scrolling.js 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850
  1. /**
  2. * @license
  3. * Copyright Google LLC All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. import { InjectionToken, Directive, forwardRef, Input, Injectable, NgZone, Optional, SkipSelf, ElementRef, NgModule, IterableDiffers, TemplateRef, ViewContainerRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Output, ViewChild, ViewEncapsulation, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
  9. import { coerceNumberProperty } from '@angular/cdk/coercion';
  10. import { Subject, fromEvent, of, Observable, animationFrameScheduler, asapScheduler, merge } from 'rxjs';
  11. import { distinctUntilChanged, auditTime, filter, takeUntil, startWith, pairwise, shareReplay, switchMap } from 'rxjs/operators';
  12. import { Platform, getRtlScrollAxisType, RtlScrollAxisType, supportsScrollBehavior, PlatformModule } from '@angular/cdk/platform';
  13. import { Directionality, BidiModule } from '@angular/cdk/bidi';
  14. import { ArrayDataSource, isDataSource } from '@angular/cdk/collections';
  15. /**
  16. * @fileoverview added by tsickle
  17. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  18. */
  19. /**
  20. * The injection token used to specify the virtual scrolling strategy.
  21. * @type {?}
  22. */
  23. const VIRTUAL_SCROLL_STRATEGY = new InjectionToken('VIRTUAL_SCROLL_STRATEGY');
  24. /**
  25. * @fileoverview added by tsickle
  26. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  27. */
  28. /**
  29. * Virtual scrolling strategy for lists with items of known fixed size.
  30. */
  31. class FixedSizeVirtualScrollStrategy {
  32. /**
  33. * @param {?} itemSize The size of the items in the virtually scrolling list.
  34. * @param {?} minBufferPx The minimum amount of buffer (in pixels) before needing to render more
  35. * @param {?} maxBufferPx The amount of buffer (in pixels) to render when rendering more.
  36. */
  37. constructor(itemSize, minBufferPx, maxBufferPx) {
  38. this._scrolledIndexChange = new Subject();
  39. /**
  40. * \@docs-private Implemented as part of VirtualScrollStrategy.
  41. */
  42. this.scrolledIndexChange = this._scrolledIndexChange.pipe(distinctUntilChanged());
  43. /**
  44. * The attached viewport.
  45. */
  46. this._viewport = null;
  47. this._itemSize = itemSize;
  48. this._minBufferPx = minBufferPx;
  49. this._maxBufferPx = maxBufferPx;
  50. }
  51. /**
  52. * Attaches this scroll strategy to a viewport.
  53. * @param {?} viewport The viewport to attach this strategy to.
  54. * @return {?}
  55. */
  56. attach(viewport) {
  57. this._viewport = viewport;
  58. this._updateTotalContentSize();
  59. this._updateRenderedRange();
  60. }
  61. /**
  62. * Detaches this scroll strategy from the currently attached viewport.
  63. * @return {?}
  64. */
  65. detach() {
  66. this._scrolledIndexChange.complete();
  67. this._viewport = null;
  68. }
  69. /**
  70. * Update the item size and buffer size.
  71. * @param {?} itemSize The size of the items in the virtually scrolling list.
  72. * @param {?} minBufferPx The minimum amount of buffer (in pixels) before needing to render more
  73. * @param {?} maxBufferPx The amount of buffer (in pixels) to render when rendering more.
  74. * @return {?}
  75. */
  76. updateItemAndBufferSize(itemSize, minBufferPx, maxBufferPx) {
  77. if (maxBufferPx < minBufferPx) {
  78. throw Error('CDK virtual scroll: maxBufferPx must be greater than or equal to minBufferPx');
  79. }
  80. this._itemSize = itemSize;
  81. this._minBufferPx = minBufferPx;
  82. this._maxBufferPx = maxBufferPx;
  83. this._updateTotalContentSize();
  84. this._updateRenderedRange();
  85. }
  86. /**
  87. * \@docs-private Implemented as part of VirtualScrollStrategy.
  88. * @return {?}
  89. */
  90. onContentScrolled() {
  91. this._updateRenderedRange();
  92. }
  93. /**
  94. * \@docs-private Implemented as part of VirtualScrollStrategy.
  95. * @return {?}
  96. */
  97. onDataLengthChanged() {
  98. this._updateTotalContentSize();
  99. this._updateRenderedRange();
  100. }
  101. /**
  102. * \@docs-private Implemented as part of VirtualScrollStrategy.
  103. * @return {?}
  104. */
  105. onContentRendered() { }
  106. /**
  107. * \@docs-private Implemented as part of VirtualScrollStrategy.
  108. * @return {?}
  109. */
  110. onRenderedOffsetChanged() { }
  111. /**
  112. * Scroll to the offset for the given index.
  113. * @param {?} index The index of the element to scroll to.
  114. * @param {?} behavior The ScrollBehavior to use when scrolling.
  115. * @return {?}
  116. */
  117. scrollToIndex(index, behavior) {
  118. if (this._viewport) {
  119. this._viewport.scrollToOffset(index * this._itemSize, behavior);
  120. }
  121. }
  122. /**
  123. * Update the viewport's total content size.
  124. * @private
  125. * @return {?}
  126. */
  127. _updateTotalContentSize() {
  128. if (!this._viewport) {
  129. return;
  130. }
  131. this._viewport.setTotalContentSize(this._viewport.getDataLength() * this._itemSize);
  132. }
  133. /**
  134. * Update the viewport's rendered range.
  135. * @private
  136. * @return {?}
  137. */
  138. _updateRenderedRange() {
  139. if (!this._viewport) {
  140. return;
  141. }
  142. /** @type {?} */
  143. const scrollOffset = this._viewport.measureScrollOffset();
  144. /** @type {?} */
  145. const firstVisibleIndex = scrollOffset / this._itemSize;
  146. /** @type {?} */
  147. const renderedRange = this._viewport.getRenderedRange();
  148. /** @type {?} */
  149. const newRange = { start: renderedRange.start, end: renderedRange.end };
  150. /** @type {?} */
  151. const viewportSize = this._viewport.getViewportSize();
  152. /** @type {?} */
  153. const dataLength = this._viewport.getDataLength();
  154. /** @type {?} */
  155. const startBuffer = scrollOffset - newRange.start * this._itemSize;
  156. if (startBuffer < this._minBufferPx && newRange.start != 0) {
  157. /** @type {?} */
  158. const expandStart = Math.ceil((this._maxBufferPx - startBuffer) / this._itemSize);
  159. newRange.start = Math.max(0, newRange.start - expandStart);
  160. newRange.end = Math.min(dataLength, Math.ceil(firstVisibleIndex + (viewportSize + this._minBufferPx) / this._itemSize));
  161. }
  162. else {
  163. /** @type {?} */
  164. const endBuffer = newRange.end * this._itemSize - (scrollOffset + viewportSize);
  165. if (endBuffer < this._minBufferPx && newRange.end != dataLength) {
  166. /** @type {?} */
  167. const expandEnd = Math.ceil((this._maxBufferPx - endBuffer) / this._itemSize);
  168. if (expandEnd > 0) {
  169. newRange.end = Math.min(dataLength, newRange.end + expandEnd);
  170. newRange.start = Math.max(0, Math.floor(firstVisibleIndex - this._minBufferPx / this._itemSize));
  171. }
  172. }
  173. }
  174. this._viewport.setRenderedRange(newRange);
  175. this._viewport.setRenderedContentOffset(this._itemSize * newRange.start);
  176. this._scrolledIndexChange.next(Math.floor(firstVisibleIndex));
  177. }
  178. }
  179. /**
  180. * Provider factory for `FixedSizeVirtualScrollStrategy` that simply extracts the already created
  181. * `FixedSizeVirtualScrollStrategy` from the given directive.
  182. * @param {?} fixedSizeDir The instance of `CdkFixedSizeVirtualScroll` to extract the
  183. * `FixedSizeVirtualScrollStrategy` from.
  184. * @return {?}
  185. */
  186. function _fixedSizeVirtualScrollStrategyFactory(fixedSizeDir) {
  187. return fixedSizeDir._scrollStrategy;
  188. }
  189. /**
  190. * A virtual scroll strategy that supports fixed-size items.
  191. */
  192. class CdkFixedSizeVirtualScroll {
  193. constructor() {
  194. this._itemSize = 20;
  195. this._minBufferPx = 100;
  196. this._maxBufferPx = 200;
  197. /**
  198. * The scroll strategy used by this directive.
  199. */
  200. this._scrollStrategy = new FixedSizeVirtualScrollStrategy(this.itemSize, this.minBufferPx, this.maxBufferPx);
  201. }
  202. /**
  203. * The size of the items in the list (in pixels).
  204. * @return {?}
  205. */
  206. get itemSize() { return this._itemSize; }
  207. /**
  208. * @param {?} value
  209. * @return {?}
  210. */
  211. set itemSize(value) { this._itemSize = coerceNumberProperty(value); }
  212. /**
  213. * The minimum amount of buffer rendered beyond the viewport (in pixels).
  214. * If the amount of buffer dips below this number, more items will be rendered. Defaults to 100px.
  215. * @return {?}
  216. */
  217. get minBufferPx() { return this._minBufferPx; }
  218. /**
  219. * @param {?} value
  220. * @return {?}
  221. */
  222. set minBufferPx(value) { this._minBufferPx = coerceNumberProperty(value); }
  223. /**
  224. * The number of pixels worth of buffer to render for when rendering new items. Defaults to 200px.
  225. * @return {?}
  226. */
  227. get maxBufferPx() { return this._maxBufferPx; }
  228. /**
  229. * @param {?} value
  230. * @return {?}
  231. */
  232. set maxBufferPx(value) { this._maxBufferPx = coerceNumberProperty(value); }
  233. /**
  234. * @return {?}
  235. */
  236. ngOnChanges() {
  237. this._scrollStrategy.updateItemAndBufferSize(this.itemSize, this.minBufferPx, this.maxBufferPx);
  238. }
  239. }
  240. CdkFixedSizeVirtualScroll.decorators = [
  241. { type: Directive, args: [{
  242. selector: 'cdk-virtual-scroll-viewport[itemSize]',
  243. providers: [{
  244. provide: VIRTUAL_SCROLL_STRATEGY,
  245. useFactory: _fixedSizeVirtualScrollStrategyFactory,
  246. deps: [forwardRef((/**
  247. * @return {?}
  248. */
  249. () => CdkFixedSizeVirtualScroll))],
  250. }],
  251. },] },
  252. ];
  253. CdkFixedSizeVirtualScroll.propDecorators = {
  254. itemSize: [{ type: Input }],
  255. minBufferPx: [{ type: Input }],
  256. maxBufferPx: [{ type: Input }]
  257. };
  258. /**
  259. * @fileoverview added by tsickle
  260. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  261. */
  262. /**
  263. * Time in ms to throttle the scrolling events by default.
  264. * @type {?}
  265. */
  266. const DEFAULT_SCROLL_TIME = 20;
  267. /**
  268. * Service contained all registered Scrollable references and emits an event when any one of the
  269. * Scrollable references emit a scrolled event.
  270. */
  271. class ScrollDispatcher {
  272. /**
  273. * @param {?} _ngZone
  274. * @param {?} _platform
  275. */
  276. constructor(_ngZone, _platform) {
  277. this._ngZone = _ngZone;
  278. this._platform = _platform;
  279. /**
  280. * Subject for notifying that a registered scrollable reference element has been scrolled.
  281. */
  282. this._scrolled = new Subject();
  283. /**
  284. * Keeps track of the global `scroll` and `resize` subscriptions.
  285. */
  286. this._globalSubscription = null;
  287. /**
  288. * Keeps track of the amount of subscriptions to `scrolled`. Used for cleaning up afterwards.
  289. */
  290. this._scrolledCount = 0;
  291. /**
  292. * Map of all the scrollable references that are registered with the service and their
  293. * scroll event subscriptions.
  294. */
  295. this.scrollContainers = new Map();
  296. }
  297. /**
  298. * Registers a scrollable instance with the service and listens for its scrolled events. When the
  299. * scrollable is scrolled, the service emits the event to its scrolled observable.
  300. * @param {?} scrollable Scrollable instance to be registered.
  301. * @return {?}
  302. */
  303. register(scrollable) {
  304. if (!this.scrollContainers.has(scrollable)) {
  305. this.scrollContainers.set(scrollable, scrollable.elementScrolled()
  306. .subscribe((/**
  307. * @return {?}
  308. */
  309. () => this._scrolled.next(scrollable))));
  310. }
  311. }
  312. /**
  313. * Deregisters a Scrollable reference and unsubscribes from its scroll event observable.
  314. * @param {?} scrollable Scrollable instance to be deregistered.
  315. * @return {?}
  316. */
  317. deregister(scrollable) {
  318. /** @type {?} */
  319. const scrollableReference = this.scrollContainers.get(scrollable);
  320. if (scrollableReference) {
  321. scrollableReference.unsubscribe();
  322. this.scrollContainers.delete(scrollable);
  323. }
  324. }
  325. /**
  326. * Returns an observable that emits an event whenever any of the registered Scrollable
  327. * references (or window, document, or body) fire a scrolled event. Can provide a time in ms
  328. * to override the default "throttle" time.
  329. *
  330. * **Note:** in order to avoid hitting change detection for every scroll event,
  331. * all of the events emitted from this stream will be run outside the Angular zone.
  332. * If you need to update any data bindings as a result of a scroll event, you have
  333. * to run the callback using `NgZone.run`.
  334. * @param {?=} auditTimeInMs
  335. * @return {?}
  336. */
  337. scrolled(auditTimeInMs = DEFAULT_SCROLL_TIME) {
  338. if (!this._platform.isBrowser) {
  339. return of();
  340. }
  341. return new Observable((/**
  342. * @param {?} observer
  343. * @return {?}
  344. */
  345. (observer) => {
  346. if (!this._globalSubscription) {
  347. this._addGlobalListener();
  348. }
  349. // In the case of a 0ms delay, use an observable without auditTime
  350. // since it does add a perceptible delay in processing overhead.
  351. /** @type {?} */
  352. const subscription = auditTimeInMs > 0 ?
  353. this._scrolled.pipe(auditTime(auditTimeInMs)).subscribe(observer) :
  354. this._scrolled.subscribe(observer);
  355. this._scrolledCount++;
  356. return (/**
  357. * @return {?}
  358. */
  359. () => {
  360. subscription.unsubscribe();
  361. this._scrolledCount--;
  362. if (!this._scrolledCount) {
  363. this._removeGlobalListener();
  364. }
  365. });
  366. }));
  367. }
  368. /**
  369. * @return {?}
  370. */
  371. ngOnDestroy() {
  372. this._removeGlobalListener();
  373. this.scrollContainers.forEach((/**
  374. * @param {?} _
  375. * @param {?} container
  376. * @return {?}
  377. */
  378. (_, container) => this.deregister(container)));
  379. this._scrolled.complete();
  380. }
  381. /**
  382. * Returns an observable that emits whenever any of the
  383. * scrollable ancestors of an element are scrolled.
  384. * @param {?} elementRef Element whose ancestors to listen for.
  385. * @param {?=} auditTimeInMs Time to throttle the scroll events.
  386. * @return {?}
  387. */
  388. ancestorScrolled(elementRef, auditTimeInMs) {
  389. /** @type {?} */
  390. const ancestors = this.getAncestorScrollContainers(elementRef);
  391. return this.scrolled(auditTimeInMs).pipe(filter((/**
  392. * @param {?} target
  393. * @return {?}
  394. */
  395. target => {
  396. return !target || ancestors.indexOf(target) > -1;
  397. })));
  398. }
  399. /**
  400. * Returns all registered Scrollables that contain the provided element.
  401. * @param {?} elementRef
  402. * @return {?}
  403. */
  404. getAncestorScrollContainers(elementRef) {
  405. /** @type {?} */
  406. const scrollingContainers = [];
  407. this.scrollContainers.forEach((/**
  408. * @param {?} _subscription
  409. * @param {?} scrollable
  410. * @return {?}
  411. */
  412. (_subscription, scrollable) => {
  413. if (this._scrollableContainsElement(scrollable, elementRef)) {
  414. scrollingContainers.push(scrollable);
  415. }
  416. }));
  417. return scrollingContainers;
  418. }
  419. /**
  420. * Returns true if the element is contained within the provided Scrollable.
  421. * @private
  422. * @param {?} scrollable
  423. * @param {?} elementRef
  424. * @return {?}
  425. */
  426. _scrollableContainsElement(scrollable, elementRef) {
  427. /** @type {?} */
  428. let element = elementRef.nativeElement;
  429. /** @type {?} */
  430. let scrollableElement = scrollable.getElementRef().nativeElement;
  431. // Traverse through the element parents until we reach null, checking if any of the elements
  432. // are the scrollable's element.
  433. do {
  434. if (element == scrollableElement) {
  435. return true;
  436. }
  437. } while (element = (/** @type {?} */ (element)).parentElement);
  438. return false;
  439. }
  440. /**
  441. * Sets up the global scroll listeners.
  442. * @private
  443. * @return {?}
  444. */
  445. _addGlobalListener() {
  446. this._globalSubscription = this._ngZone.runOutsideAngular((/**
  447. * @return {?}
  448. */
  449. () => {
  450. return fromEvent(window.document, 'scroll').subscribe((/**
  451. * @return {?}
  452. */
  453. () => this._scrolled.next()));
  454. }));
  455. }
  456. /**
  457. * Cleans up the global scroll listener.
  458. * @private
  459. * @return {?}
  460. */
  461. _removeGlobalListener() {
  462. if (this._globalSubscription) {
  463. this._globalSubscription.unsubscribe();
  464. this._globalSubscription = null;
  465. }
  466. }
  467. }
  468. ScrollDispatcher.decorators = [
  469. { type: Injectable, args: [{ providedIn: 'root' },] },
  470. ];
  471. /** @nocollapse */
  472. ScrollDispatcher.ctorParameters = () => [
  473. { type: NgZone },
  474. { type: Platform }
  475. ];
  476. /** @nocollapse */ ScrollDispatcher.ngInjectableDef = ɵɵdefineInjectable({ factory: function ScrollDispatcher_Factory() { return new ScrollDispatcher(ɵɵinject(NgZone), ɵɵinject(Platform)); }, token: ScrollDispatcher, providedIn: "root" });
  477. /**
  478. * \@docs-private \@deprecated \@breaking-change 8.0.0
  479. * @param {?} parentDispatcher
  480. * @param {?} ngZone
  481. * @param {?} platform
  482. * @return {?}
  483. */
  484. function SCROLL_DISPATCHER_PROVIDER_FACTORY(parentDispatcher, ngZone, platform) {
  485. return parentDispatcher || new ScrollDispatcher(ngZone, platform);
  486. }
  487. /**
  488. * \@docs-private \@deprecated \@breaking-change 8.0.0
  489. * @type {?}
  490. */
  491. const SCROLL_DISPATCHER_PROVIDER = {
  492. // If there is already a ScrollDispatcher available, use that. Otherwise, provide a new one.
  493. provide: ScrollDispatcher,
  494. deps: [[new Optional(), new SkipSelf(), ScrollDispatcher], NgZone, Platform],
  495. useFactory: SCROLL_DISPATCHER_PROVIDER_FACTORY
  496. };
  497. /**
  498. * @fileoverview added by tsickle
  499. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  500. */
  501. /**
  502. * Sends an event when the directive's element is scrolled. Registers itself with the
  503. * ScrollDispatcher service to include itself as part of its collection of scrolling events that it
  504. * can be listened to through the service.
  505. */
  506. class CdkScrollable {
  507. /**
  508. * @param {?} elementRef
  509. * @param {?} scrollDispatcher
  510. * @param {?} ngZone
  511. * @param {?=} dir
  512. */
  513. constructor(elementRef, scrollDispatcher, ngZone, dir) {
  514. this.elementRef = elementRef;
  515. this.scrollDispatcher = scrollDispatcher;
  516. this.ngZone = ngZone;
  517. this.dir = dir;
  518. this._destroyed = new Subject();
  519. this._elementScrolled = new Observable((/**
  520. * @param {?} observer
  521. * @return {?}
  522. */
  523. (observer) => this.ngZone.runOutsideAngular((/**
  524. * @return {?}
  525. */
  526. () => fromEvent(this.elementRef.nativeElement, 'scroll').pipe(takeUntil(this._destroyed))
  527. .subscribe(observer)))));
  528. }
  529. /**
  530. * @return {?}
  531. */
  532. ngOnInit() {
  533. this.scrollDispatcher.register(this);
  534. }
  535. /**
  536. * @return {?}
  537. */
  538. ngOnDestroy() {
  539. this.scrollDispatcher.deregister(this);
  540. this._destroyed.next();
  541. this._destroyed.complete();
  542. }
  543. /**
  544. * Returns observable that emits when a scroll event is fired on the host element.
  545. * @return {?}
  546. */
  547. elementScrolled() {
  548. return this._elementScrolled;
  549. }
  550. /**
  551. * Gets the ElementRef for the viewport.
  552. * @return {?}
  553. */
  554. getElementRef() {
  555. return this.elementRef;
  556. }
  557. /**
  558. * Scrolls to the specified offsets. This is a normalized version of the browser's native scrollTo
  559. * method, since browsers are not consistent about what scrollLeft means in RTL. For this method
  560. * left and right always refer to the left and right side of the scrolling container irrespective
  561. * of the layout direction. start and end refer to left and right in an LTR context and vice-versa
  562. * in an RTL context.
  563. * @param {?} options specified the offsets to scroll to.
  564. * @return {?}
  565. */
  566. scrollTo(options) {
  567. /** @type {?} */
  568. const el = this.elementRef.nativeElement;
  569. /** @type {?} */
  570. const isRtl = this.dir && this.dir.value == 'rtl';
  571. // Rewrite start & end offsets as right or left offsets.
  572. options.left = options.left == null ? (isRtl ? options.end : options.start) : options.left;
  573. options.right = options.right == null ? (isRtl ? options.start : options.end) : options.right;
  574. // Rewrite the bottom offset as a top offset.
  575. if (options.bottom != null) {
  576. ((/** @type {?} */ (options))).top =
  577. el.scrollHeight - el.clientHeight - options.bottom;
  578. }
  579. // Rewrite the right offset as a left offset.
  580. if (isRtl && getRtlScrollAxisType() != RtlScrollAxisType.NORMAL) {
  581. if (options.left != null) {
  582. ((/** @type {?} */ (options))).right =
  583. el.scrollWidth - el.clientWidth - options.left;
  584. }
  585. if (getRtlScrollAxisType() == RtlScrollAxisType.INVERTED) {
  586. options.left = options.right;
  587. }
  588. else if (getRtlScrollAxisType() == RtlScrollAxisType.NEGATED) {
  589. options.left = options.right ? -options.right : options.right;
  590. }
  591. }
  592. else {
  593. if (options.right != null) {
  594. ((/** @type {?} */ (options))).left =
  595. el.scrollWidth - el.clientWidth - options.right;
  596. }
  597. }
  598. this._applyScrollToOptions(options);
  599. }
  600. /**
  601. * @private
  602. * @param {?} options
  603. * @return {?}
  604. */
  605. _applyScrollToOptions(options) {
  606. /** @type {?} */
  607. const el = this.elementRef.nativeElement;
  608. if (supportsScrollBehavior()) {
  609. el.scrollTo(options);
  610. }
  611. else {
  612. if (options.top != null) {
  613. el.scrollTop = options.top;
  614. }
  615. if (options.left != null) {
  616. el.scrollLeft = options.left;
  617. }
  618. }
  619. }
  620. /**
  621. * Measures the scroll offset relative to the specified edge of the viewport. This method can be
  622. * used instead of directly checking scrollLeft or scrollTop, since browsers are not consistent
  623. * about what scrollLeft means in RTL. The values returned by this method are normalized such that
  624. * left and right always refer to the left and right side of the scrolling container irrespective
  625. * of the layout direction. start and end refer to left and right in an LTR context and vice-versa
  626. * in an RTL context.
  627. * @param {?} from The edge to measure from.
  628. * @return {?}
  629. */
  630. measureScrollOffset(from) {
  631. /** @type {?} */
  632. const LEFT = 'left';
  633. /** @type {?} */
  634. const RIGHT = 'right';
  635. /** @type {?} */
  636. const el = this.elementRef.nativeElement;
  637. if (from == 'top') {
  638. return el.scrollTop;
  639. }
  640. if (from == 'bottom') {
  641. return el.scrollHeight - el.clientHeight - el.scrollTop;
  642. }
  643. // Rewrite start & end as left or right offsets.
  644. /** @type {?} */
  645. const isRtl = this.dir && this.dir.value == 'rtl';
  646. if (from == 'start') {
  647. from = isRtl ? RIGHT : LEFT;
  648. }
  649. else if (from == 'end') {
  650. from = isRtl ? LEFT : RIGHT;
  651. }
  652. if (isRtl && getRtlScrollAxisType() == RtlScrollAxisType.INVERTED) {
  653. // For INVERTED, scrollLeft is (scrollWidth - clientWidth) when scrolled all the way left and
  654. // 0 when scrolled all the way right.
  655. if (from == LEFT) {
  656. return el.scrollWidth - el.clientWidth - el.scrollLeft;
  657. }
  658. else {
  659. return el.scrollLeft;
  660. }
  661. }
  662. else if (isRtl && getRtlScrollAxisType() == RtlScrollAxisType.NEGATED) {
  663. // For NEGATED, scrollLeft is -(scrollWidth - clientWidth) when scrolled all the way left and
  664. // 0 when scrolled all the way right.
  665. if (from == LEFT) {
  666. return el.scrollLeft + el.scrollWidth - el.clientWidth;
  667. }
  668. else {
  669. return -el.scrollLeft;
  670. }
  671. }
  672. else {
  673. // For NORMAL, as well as non-RTL contexts, scrollLeft is 0 when scrolled all the way left and
  674. // (scrollWidth - clientWidth) when scrolled all the way right.
  675. if (from == LEFT) {
  676. return el.scrollLeft;
  677. }
  678. else {
  679. return el.scrollWidth - el.clientWidth - el.scrollLeft;
  680. }
  681. }
  682. }
  683. }
  684. CdkScrollable.decorators = [
  685. { type: Directive, args: [{
  686. selector: '[cdk-scrollable], [cdkScrollable]'
  687. },] },
  688. ];
  689. /** @nocollapse */
  690. CdkScrollable.ctorParameters = () => [
  691. { type: ElementRef },
  692. { type: ScrollDispatcher },
  693. { type: NgZone },
  694. { type: Directionality, decorators: [{ type: Optional }] }
  695. ];
  696. /**
  697. * @fileoverview added by tsickle
  698. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  699. */
  700. /**
  701. * Checks if the given ranges are equal.
  702. * @param {?} r1
  703. * @param {?} r2
  704. * @return {?}
  705. */
  706. function rangesEqual(r1, r2) {
  707. return r1.start == r2.start && r1.end == r2.end;
  708. }
  709. /**
  710. * Scheduler to be used for scroll events. Needs to fall back to
  711. * something that doesn't rely on requestAnimationFrame on environments
  712. * that don't support it (e.g. server-side rendering).
  713. * @type {?}
  714. */
  715. const SCROLL_SCHEDULER = typeof requestAnimationFrame !== 'undefined' ? animationFrameScheduler : asapScheduler;
  716. /**
  717. * A viewport that virtualizes its scrolling with the help of `CdkVirtualForOf`.
  718. */
  719. class CdkVirtualScrollViewport extends CdkScrollable {
  720. /**
  721. * @param {?} elementRef
  722. * @param {?} _changeDetectorRef
  723. * @param {?} ngZone
  724. * @param {?} _scrollStrategy
  725. * @param {?} dir
  726. * @param {?} scrollDispatcher
  727. */
  728. constructor(elementRef, _changeDetectorRef, ngZone, _scrollStrategy, dir, scrollDispatcher) {
  729. super(elementRef, scrollDispatcher, ngZone, dir);
  730. this.elementRef = elementRef;
  731. this._changeDetectorRef = _changeDetectorRef;
  732. this._scrollStrategy = _scrollStrategy;
  733. /**
  734. * Emits when the viewport is detached from a CdkVirtualForOf.
  735. */
  736. this._detachedSubject = new Subject();
  737. /**
  738. * Emits when the rendered range changes.
  739. */
  740. this._renderedRangeSubject = new Subject();
  741. this._orientation = 'vertical';
  742. // Note: we don't use the typical EventEmitter here because we need to subscribe to the scroll
  743. // strategy lazily (i.e. only if the user is actually listening to the events). We do this because
  744. // depending on how the strategy calculates the scrolled index, it may come at a cost to
  745. // performance.
  746. /**
  747. * Emits when the index of the first element visible in the viewport changes.
  748. */
  749. this.scrolledIndexChange = new Observable((/**
  750. * @param {?} observer
  751. * @return {?}
  752. */
  753. (observer) => this._scrollStrategy.scrolledIndexChange.subscribe((/**
  754. * @param {?} index
  755. * @return {?}
  756. */
  757. index => Promise.resolve().then((/**
  758. * @return {?}
  759. */
  760. () => this.ngZone.run((/**
  761. * @return {?}
  762. */
  763. () => observer.next(index)))))))));
  764. /**
  765. * A stream that emits whenever the rendered range changes.
  766. */
  767. this.renderedRangeStream = this._renderedRangeSubject.asObservable();
  768. /**
  769. * The total size of all content (in pixels), including content that is not currently rendered.
  770. */
  771. this._totalContentSize = 0;
  772. /**
  773. * A string representing the `style.width` property value to be used for the spacer element.
  774. */
  775. this._totalContentWidth = '';
  776. /**
  777. * A string representing the `style.height` property value to be used for the spacer element.
  778. */
  779. this._totalContentHeight = '';
  780. /**
  781. * The currently rendered range of indices.
  782. */
  783. this._renderedRange = { start: 0, end: 0 };
  784. /**
  785. * The length of the data bound to this viewport (in number of items).
  786. */
  787. this._dataLength = 0;
  788. /**
  789. * The size of the viewport (in pixels).
  790. */
  791. this._viewportSize = 0;
  792. /**
  793. * The last rendered content offset that was set.
  794. */
  795. this._renderedContentOffset = 0;
  796. /**
  797. * Whether the last rendered content offset was to the end of the content (and therefore needs to
  798. * be rewritten as an offset to the start of the content).
  799. */
  800. this._renderedContentOffsetNeedsRewrite = false;
  801. /**
  802. * Whether there is a pending change detection cycle.
  803. */
  804. this._isChangeDetectionPending = false;
  805. /**
  806. * A list of functions to run after the next change detection cycle.
  807. */
  808. this._runAfterChangeDetection = [];
  809. if (!_scrollStrategy) {
  810. throw Error('Error: cdk-virtual-scroll-viewport requires the "itemSize" property to be set.');
  811. }
  812. }
  813. /**
  814. * The direction the viewport scrolls.
  815. * @return {?}
  816. */
  817. get orientation() {
  818. return this._orientation;
  819. }
  820. /**
  821. * @param {?} orientation
  822. * @return {?}
  823. */
  824. set orientation(orientation) {
  825. if (this._orientation !== orientation) {
  826. this._orientation = orientation;
  827. this._calculateSpacerSize();
  828. }
  829. }
  830. /**
  831. * @return {?}
  832. */
  833. ngOnInit() {
  834. super.ngOnInit();
  835. // It's still too early to measure the viewport at this point. Deferring with a promise allows
  836. // the Viewport to be rendered with the correct size before we measure. We run this outside the
  837. // zone to avoid causing more change detection cycles. We handle the change detection loop
  838. // ourselves instead.
  839. this.ngZone.runOutsideAngular((/**
  840. * @return {?}
  841. */
  842. () => Promise.resolve().then((/**
  843. * @return {?}
  844. */
  845. () => {
  846. this._measureViewportSize();
  847. this._scrollStrategy.attach(this);
  848. this.elementScrolled()
  849. .pipe(
  850. // Start off with a fake scroll event so we properly detect our initial position.
  851. startWith((/** @type {?} */ (null))),
  852. // Collect multiple events into one until the next animation frame. This way if
  853. // there are multiple scroll events in the same frame we only need to recheck
  854. // our layout once.
  855. auditTime(0, SCROLL_SCHEDULER))
  856. .subscribe((/**
  857. * @return {?}
  858. */
  859. () => this._scrollStrategy.onContentScrolled()));
  860. this._markChangeDetectionNeeded();
  861. }))));
  862. }
  863. /**
  864. * @return {?}
  865. */
  866. ngOnDestroy() {
  867. this.detach();
  868. this._scrollStrategy.detach();
  869. // Complete all subjects
  870. this._renderedRangeSubject.complete();
  871. this._detachedSubject.complete();
  872. super.ngOnDestroy();
  873. }
  874. /**
  875. * Attaches a `CdkVirtualForOf` to this viewport.
  876. * @param {?} forOf
  877. * @return {?}
  878. */
  879. attach(forOf) {
  880. if (this._forOf) {
  881. throw Error('CdkVirtualScrollViewport is already attached.');
  882. }
  883. // Subscribe to the data stream of the CdkVirtualForOf to keep track of when the data length
  884. // changes. Run outside the zone to avoid triggering change detection, since we're managing the
  885. // change detection loop ourselves.
  886. this.ngZone.runOutsideAngular((/**
  887. * @return {?}
  888. */
  889. () => {
  890. this._forOf = forOf;
  891. this._forOf.dataStream.pipe(takeUntil(this._detachedSubject)).subscribe((/**
  892. * @param {?} data
  893. * @return {?}
  894. */
  895. data => {
  896. /** @type {?} */
  897. const newLength = data.length;
  898. if (newLength !== this._dataLength) {
  899. this._dataLength = newLength;
  900. this._scrollStrategy.onDataLengthChanged();
  901. }
  902. this._doChangeDetection();
  903. }));
  904. }));
  905. }
  906. /**
  907. * Detaches the current `CdkVirtualForOf`.
  908. * @return {?}
  909. */
  910. detach() {
  911. this._forOf = null;
  912. this._detachedSubject.next();
  913. }
  914. /**
  915. * Gets the length of the data bound to this viewport (in number of items).
  916. * @return {?}
  917. */
  918. getDataLength() {
  919. return this._dataLength;
  920. }
  921. /**
  922. * Gets the size of the viewport (in pixels).
  923. * @return {?}
  924. */
  925. getViewportSize() {
  926. return this._viewportSize;
  927. }
  928. // TODO(mmalerba): This is technically out of sync with what's really rendered until a render
  929. // cycle happens. I'm being careful to only call it after the render cycle is complete and before
  930. // setting it to something else, but its error prone and should probably be split into
  931. // `pendingRange` and `renderedRange`, the latter reflecting whats actually in the DOM.
  932. /**
  933. * Get the current rendered range of items.
  934. * @return {?}
  935. */
  936. getRenderedRange() {
  937. return this._renderedRange;
  938. }
  939. /**
  940. * Sets the total size of all content (in pixels), including content that is not currently
  941. * rendered.
  942. * @param {?} size
  943. * @return {?}
  944. */
  945. setTotalContentSize(size) {
  946. if (this._totalContentSize !== size) {
  947. this._totalContentSize = size;
  948. this._calculateSpacerSize();
  949. this._markChangeDetectionNeeded();
  950. }
  951. }
  952. /**
  953. * Sets the currently rendered range of indices.
  954. * @param {?} range
  955. * @return {?}
  956. */
  957. setRenderedRange(range) {
  958. if (!rangesEqual(this._renderedRange, range)) {
  959. this._renderedRangeSubject.next(this._renderedRange = range);
  960. this._markChangeDetectionNeeded((/**
  961. * @return {?}
  962. */
  963. () => this._scrollStrategy.onContentRendered()));
  964. }
  965. }
  966. /**
  967. * Gets the offset from the start of the viewport to the start of the rendered data (in pixels).
  968. * @return {?}
  969. */
  970. getOffsetToRenderedContentStart() {
  971. return this._renderedContentOffsetNeedsRewrite ? null : this._renderedContentOffset;
  972. }
  973. /**
  974. * Sets the offset from the start of the viewport to either the start or end of the rendered data
  975. * (in pixels).
  976. * @param {?} offset
  977. * @param {?=} to
  978. * @return {?}
  979. */
  980. setRenderedContentOffset(offset, to = 'to-start') {
  981. // For a horizontal viewport in a right-to-left language we need to translate along the x-axis
  982. // in the negative direction.
  983. /** @type {?} */
  984. const isRtl = this.dir && this.dir.value == 'rtl';
  985. /** @type {?} */
  986. const isHorizontal = this.orientation == 'horizontal';
  987. /** @type {?} */
  988. const axis = isHorizontal ? 'X' : 'Y';
  989. /** @type {?} */
  990. const axisDirection = isHorizontal && isRtl ? -1 : 1;
  991. /** @type {?} */
  992. let transform = `translate${axis}(${Number(axisDirection * offset)}px)`;
  993. this._renderedContentOffset = offset;
  994. if (to === 'to-end') {
  995. transform += ` translate${axis}(-100%)`;
  996. // The viewport should rewrite this as a `to-start` offset on the next render cycle. Otherwise
  997. // elements will appear to expand in the wrong direction (e.g. `mat-expansion-panel` would
  998. // expand upward).
  999. this._renderedContentOffsetNeedsRewrite = true;
  1000. }
  1001. if (this._renderedContentTransform != transform) {
  1002. // We know this value is safe because we parse `offset` with `Number()` before passing it
  1003. // into the string.
  1004. this._renderedContentTransform = transform;
  1005. this._markChangeDetectionNeeded((/**
  1006. * @return {?}
  1007. */
  1008. () => {
  1009. if (this._renderedContentOffsetNeedsRewrite) {
  1010. this._renderedContentOffset -= this.measureRenderedContentSize();
  1011. this._renderedContentOffsetNeedsRewrite = false;
  1012. this.setRenderedContentOffset(this._renderedContentOffset);
  1013. }
  1014. else {
  1015. this._scrollStrategy.onRenderedOffsetChanged();
  1016. }
  1017. }));
  1018. }
  1019. }
  1020. /**
  1021. * Scrolls to the given offset from the start of the viewport. Please note that this is not always
  1022. * the same as setting `scrollTop` or `scrollLeft`. In a horizontal viewport with right-to-left
  1023. * direction, this would be the equivalent of setting a fictional `scrollRight` property.
  1024. * @param {?} offset The offset to scroll to.
  1025. * @param {?=} behavior The ScrollBehavior to use when scrolling. Default is behavior is `auto`.
  1026. * @return {?}
  1027. */
  1028. scrollToOffset(offset, behavior = 'auto') {
  1029. /** @type {?} */
  1030. const options = { behavior };
  1031. if (this.orientation === 'horizontal') {
  1032. options.start = offset;
  1033. }
  1034. else {
  1035. options.top = offset;
  1036. }
  1037. this.scrollTo(options);
  1038. }
  1039. /**
  1040. * Scrolls to the offset for the given index.
  1041. * @param {?} index The index of the element to scroll to.
  1042. * @param {?=} behavior The ScrollBehavior to use when scrolling. Default is behavior is `auto`.
  1043. * @return {?}
  1044. */
  1045. scrollToIndex(index, behavior = 'auto') {
  1046. this._scrollStrategy.scrollToIndex(index, behavior);
  1047. }
  1048. /**
  1049. * Gets the current scroll offset from the start of the viewport (in pixels).
  1050. * @param {?=} from The edge to measure the offset from. Defaults to 'top' in vertical mode and 'start'
  1051. * in horizontal mode.
  1052. * @return {?}
  1053. */
  1054. measureScrollOffset(from) {
  1055. return super.measureScrollOffset(from ? from : this.orientation === 'horizontal' ? 'start' : 'top');
  1056. }
  1057. /**
  1058. * Measure the combined size of all of the rendered items.
  1059. * @return {?}
  1060. */
  1061. measureRenderedContentSize() {
  1062. /** @type {?} */
  1063. const contentEl = this._contentWrapper.nativeElement;
  1064. return this.orientation === 'horizontal' ? contentEl.offsetWidth : contentEl.offsetHeight;
  1065. }
  1066. /**
  1067. * Measure the total combined size of the given range. Throws if the range includes items that are
  1068. * not rendered.
  1069. * @param {?} range
  1070. * @return {?}
  1071. */
  1072. measureRangeSize(range) {
  1073. if (!this._forOf) {
  1074. return 0;
  1075. }
  1076. return this._forOf.measureRangeSize(range, this.orientation);
  1077. }
  1078. /**
  1079. * Update the viewport dimensions and re-render.
  1080. * @return {?}
  1081. */
  1082. checkViewportSize() {
  1083. // TODO: Cleanup later when add logic for handling content resize
  1084. this._measureViewportSize();
  1085. this._scrollStrategy.onDataLengthChanged();
  1086. }
  1087. /**
  1088. * Measure the viewport size.
  1089. * @private
  1090. * @return {?}
  1091. */
  1092. _measureViewportSize() {
  1093. /** @type {?} */
  1094. const viewportEl = this.elementRef.nativeElement;
  1095. this._viewportSize = this.orientation === 'horizontal' ?
  1096. viewportEl.clientWidth : viewportEl.clientHeight;
  1097. }
  1098. /**
  1099. * Queue up change detection to run.
  1100. * @private
  1101. * @param {?=} runAfter
  1102. * @return {?}
  1103. */
  1104. _markChangeDetectionNeeded(runAfter) {
  1105. if (runAfter) {
  1106. this._runAfterChangeDetection.push(runAfter);
  1107. }
  1108. // Use a Promise to batch together calls to `_doChangeDetection`. This way if we set a bunch of
  1109. // properties sequentially we only have to run `_doChangeDetection` once at the end.
  1110. if (!this._isChangeDetectionPending) {
  1111. this._isChangeDetectionPending = true;
  1112. this.ngZone.runOutsideAngular((/**
  1113. * @return {?}
  1114. */
  1115. () => Promise.resolve().then((/**
  1116. * @return {?}
  1117. */
  1118. () => {
  1119. this._doChangeDetection();
  1120. }))));
  1121. }
  1122. }
  1123. /**
  1124. * Run change detection.
  1125. * @private
  1126. * @return {?}
  1127. */
  1128. _doChangeDetection() {
  1129. this._isChangeDetectionPending = false;
  1130. // Apply changes to Angular bindings. Note: We must call `markForCheck` to run change detection
  1131. // from the root, since the repeated items are content projected in. Calling `detectChanges`
  1132. // instead does not properly check the projected content.
  1133. this.ngZone.run((/**
  1134. * @return {?}
  1135. */
  1136. () => this._changeDetectorRef.markForCheck()));
  1137. // Apply the content transform. The transform can't be set via an Angular binding because
  1138. // bypassSecurityTrustStyle is banned in Google. However the value is safe, it's composed of
  1139. // string literals, a variable that can only be 'X' or 'Y', and user input that is run through
  1140. // the `Number` function first to coerce it to a numeric value.
  1141. this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform;
  1142. /** @type {?} */
  1143. const runAfterChangeDetection = this._runAfterChangeDetection;
  1144. this._runAfterChangeDetection = [];
  1145. for (const fn of runAfterChangeDetection) {
  1146. fn();
  1147. }
  1148. }
  1149. /**
  1150. * Calculates the `style.width` and `style.height` for the spacer element.
  1151. * @private
  1152. * @return {?}
  1153. */
  1154. _calculateSpacerSize() {
  1155. this._totalContentHeight =
  1156. this.orientation === 'horizontal' ? '' : `${this._totalContentSize}px`;
  1157. this._totalContentWidth =
  1158. this.orientation === 'horizontal' ? `${this._totalContentSize}px` : '';
  1159. }
  1160. }
  1161. CdkVirtualScrollViewport.decorators = [
  1162. { type: Component, args: [{selector: 'cdk-virtual-scroll-viewport',
  1163. template: "<div #contentWrapper class=\"cdk-virtual-scroll-content-wrapper\"><ng-content></ng-content></div><div class=\"cdk-virtual-scroll-spacer\" [style.width]=\"_totalContentWidth\" [style.height]=\"_totalContentHeight\"></div>",
  1164. styles: ["cdk-virtual-scroll-viewport{display:block;position:relative;overflow:auto;contain:strict;transform:translateZ(0);will-change:scroll-position;-webkit-overflow-scrolling:touch}.cdk-virtual-scroll-content-wrapper{position:absolute;top:0;left:0;contain:content}[dir=rtl] .cdk-virtual-scroll-content-wrapper{right:0;left:auto}.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper{min-height:100%}.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>dl:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>ol:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>table:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>ul:not([cdkVirtualFor]){padding-left:0;padding-right:0;margin-left:0;margin-right:0;border-left-width:0;border-right-width:0;outline:0}.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper{min-width:100%}.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>dl:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>ol:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>table:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>ul:not([cdkVirtualFor]){padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;border-top-width:0;border-bottom-width:0;outline:0}.cdk-virtual-scroll-spacer{position:absolute;top:0;left:0;height:1px;width:1px;transform-origin:0 0}[dir=rtl] .cdk-virtual-scroll-spacer{right:0;left:auto;transform-origin:100% 0}"],
  1165. host: {
  1166. 'class': 'cdk-virtual-scroll-viewport',
  1167. '[class.cdk-virtual-scroll-orientation-horizontal]': 'orientation === "horizontal"',
  1168. '[class.cdk-virtual-scroll-orientation-vertical]': 'orientation !== "horizontal"',
  1169. },
  1170. encapsulation: ViewEncapsulation.None,
  1171. changeDetection: ChangeDetectionStrategy.OnPush,
  1172. providers: [{
  1173. provide: CdkScrollable,
  1174. useExisting: CdkVirtualScrollViewport,
  1175. }]
  1176. },] },
  1177. ];
  1178. /** @nocollapse */
  1179. CdkVirtualScrollViewport.ctorParameters = () => [
  1180. { type: ElementRef },
  1181. { type: ChangeDetectorRef },
  1182. { type: NgZone },
  1183. { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [VIRTUAL_SCROLL_STRATEGY,] }] },
  1184. { type: Directionality, decorators: [{ type: Optional }] },
  1185. { type: ScrollDispatcher }
  1186. ];
  1187. CdkVirtualScrollViewport.propDecorators = {
  1188. orientation: [{ type: Input }],
  1189. scrolledIndexChange: [{ type: Output }],
  1190. _contentWrapper: [{ type: ViewChild, args: ['contentWrapper', { static: true },] }]
  1191. };
  1192. /**
  1193. * @fileoverview added by tsickle
  1194. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1195. */
  1196. /**
  1197. * Helper to extract size from a DOM Node.
  1198. * @param {?} orientation
  1199. * @param {?} node
  1200. * @return {?}
  1201. */
  1202. function getSize(orientation, node) {
  1203. /** @type {?} */
  1204. const el = (/** @type {?} */ (node));
  1205. if (!el.getBoundingClientRect) {
  1206. return 0;
  1207. }
  1208. /** @type {?} */
  1209. const rect = el.getBoundingClientRect();
  1210. return orientation == 'horizontal' ? rect.width : rect.height;
  1211. }
  1212. /**
  1213. * A directive similar to `ngForOf` to be used for rendering data inside a virtual scrolling
  1214. * container.
  1215. * @template T
  1216. */
  1217. class CdkVirtualForOf {
  1218. /**
  1219. * @param {?} _viewContainerRef
  1220. * @param {?} _template
  1221. * @param {?} _differs
  1222. * @param {?} _viewport
  1223. * @param {?} ngZone
  1224. */
  1225. constructor(_viewContainerRef, _template, _differs, _viewport, ngZone) {
  1226. this._viewContainerRef = _viewContainerRef;
  1227. this._template = _template;
  1228. this._differs = _differs;
  1229. this._viewport = _viewport;
  1230. /**
  1231. * Emits when the rendered view of the data changes.
  1232. */
  1233. this.viewChange = new Subject();
  1234. /**
  1235. * Subject that emits when a new DataSource instance is given.
  1236. */
  1237. this._dataSourceChanges = new Subject();
  1238. /**
  1239. * The size of the cache used to store templates that are not being used for re-use later.
  1240. * Setting the cache size to `0` will disable caching. Defaults to 20 templates.
  1241. */
  1242. this.cdkVirtualForTemplateCacheSize = 20;
  1243. /**
  1244. * Emits whenever the data in the current DataSource changes.
  1245. */
  1246. this.dataStream = this._dataSourceChanges
  1247. .pipe(
  1248. // Start off with null `DataSource`.
  1249. startWith((/** @type {?} */ (null))),
  1250. // Bundle up the previous and current data sources so we can work with both.
  1251. pairwise(),
  1252. // Use `_changeDataSource` to disconnect from the previous data source and connect to the
  1253. // new one, passing back a stream of data changes which we run through `switchMap` to give
  1254. // us a data stream that emits the latest data from whatever the current `DataSource` is.
  1255. switchMap((/**
  1256. * @param {?} __0
  1257. * @return {?}
  1258. */
  1259. ([prev, cur]) => this._changeDataSource(prev, cur))),
  1260. // Replay the last emitted data when someone subscribes.
  1261. shareReplay(1));
  1262. /**
  1263. * The differ used to calculate changes to the data.
  1264. */
  1265. this._differ = null;
  1266. /**
  1267. * The template cache used to hold on ot template instancess that have been stamped out, but don't
  1268. * currently need to be rendered. These instances will be reused in the future rather than
  1269. * stamping out brand new ones.
  1270. */
  1271. this._templateCache = [];
  1272. /**
  1273. * Whether the rendered data should be updated during the next ngDoCheck cycle.
  1274. */
  1275. this._needsUpdate = false;
  1276. this._destroyed = new Subject();
  1277. this.dataStream.subscribe((/**
  1278. * @param {?} data
  1279. * @return {?}
  1280. */
  1281. data => {
  1282. this._data = data;
  1283. this._onRenderedDataChange();
  1284. }));
  1285. this._viewport.renderedRangeStream.pipe(takeUntil(this._destroyed)).subscribe((/**
  1286. * @param {?} range
  1287. * @return {?}
  1288. */
  1289. range => {
  1290. this._renderedRange = range;
  1291. ngZone.run((/**
  1292. * @return {?}
  1293. */
  1294. () => this.viewChange.next(this._renderedRange)));
  1295. this._onRenderedDataChange();
  1296. }));
  1297. this._viewport.attach(this);
  1298. }
  1299. /**
  1300. * The DataSource to display.
  1301. * @return {?}
  1302. */
  1303. get cdkVirtualForOf() {
  1304. return this._cdkVirtualForOf;
  1305. }
  1306. /**
  1307. * @param {?} value
  1308. * @return {?}
  1309. */
  1310. set cdkVirtualForOf(value) {
  1311. this._cdkVirtualForOf = value;
  1312. /** @type {?} */
  1313. const ds = isDataSource(value) ? value :
  1314. // Slice the value if its an NgIterable to ensure we're working with an array.
  1315. new ArrayDataSource(value instanceof Observable ? value : Array.prototype.slice.call(value || []));
  1316. this._dataSourceChanges.next(ds);
  1317. }
  1318. /**
  1319. * The `TrackByFunction` to use for tracking changes. The `TrackByFunction` takes the index and
  1320. * the item and produces a value to be used as the item's identity when tracking changes.
  1321. * @return {?}
  1322. */
  1323. get cdkVirtualForTrackBy() {
  1324. return this._cdkVirtualForTrackBy;
  1325. }
  1326. /**
  1327. * @param {?} fn
  1328. * @return {?}
  1329. */
  1330. set cdkVirtualForTrackBy(fn) {
  1331. this._needsUpdate = true;
  1332. this._cdkVirtualForTrackBy = fn ?
  1333. (/**
  1334. * @param {?} index
  1335. * @param {?} item
  1336. * @return {?}
  1337. */
  1338. (index, item) => fn(index + (this._renderedRange ? this._renderedRange.start : 0), item)) :
  1339. undefined;
  1340. }
  1341. /**
  1342. * The template used to stamp out new elements.
  1343. * @param {?} value
  1344. * @return {?}
  1345. */
  1346. set cdkVirtualForTemplate(value) {
  1347. if (value) {
  1348. this._needsUpdate = true;
  1349. this._template = value;
  1350. }
  1351. }
  1352. /**
  1353. * Measures the combined size (width for horizontal orientation, height for vertical) of all items
  1354. * in the specified range. Throws an error if the range includes items that are not currently
  1355. * rendered.
  1356. * @param {?} range
  1357. * @param {?} orientation
  1358. * @return {?}
  1359. */
  1360. measureRangeSize(range, orientation) {
  1361. if (range.start >= range.end) {
  1362. return 0;
  1363. }
  1364. if (range.start < this._renderedRange.start || range.end > this._renderedRange.end) {
  1365. throw Error(`Error: attempted to measure an item that isn't rendered.`);
  1366. }
  1367. // The index into the list of rendered views for the first item in the range.
  1368. /** @type {?} */
  1369. const renderedStartIndex = range.start - this._renderedRange.start;
  1370. // The length of the range we're measuring.
  1371. /** @type {?} */
  1372. const rangeLen = range.end - range.start;
  1373. // Loop over all root nodes for all items in the range and sum up their size.
  1374. /** @type {?} */
  1375. let totalSize = 0;
  1376. /** @type {?} */
  1377. let i = rangeLen;
  1378. while (i--) {
  1379. /** @type {?} */
  1380. const view = (/** @type {?} */ (this._viewContainerRef.get(i + renderedStartIndex)));
  1381. /** @type {?} */
  1382. let j = view ? view.rootNodes.length : 0;
  1383. while (j--) {
  1384. totalSize += getSize(orientation, (/** @type {?} */ (view)).rootNodes[j]);
  1385. }
  1386. }
  1387. return totalSize;
  1388. }
  1389. /**
  1390. * @return {?}
  1391. */
  1392. ngDoCheck() {
  1393. if (this._differ && this._needsUpdate) {
  1394. // TODO(mmalerba): We should differentiate needs update due to scrolling and a new portion of
  1395. // this list being rendered (can use simpler algorithm) vs needs update due to data actually
  1396. // changing (need to do this diff).
  1397. /** @type {?} */
  1398. const changes = this._differ.diff(this._renderedItems);
  1399. if (!changes) {
  1400. this._updateContext();
  1401. }
  1402. else {
  1403. this._applyChanges(changes);
  1404. }
  1405. this._needsUpdate = false;
  1406. }
  1407. }
  1408. /**
  1409. * @return {?}
  1410. */
  1411. ngOnDestroy() {
  1412. this._viewport.detach();
  1413. this._dataSourceChanges.next();
  1414. this._dataSourceChanges.complete();
  1415. this.viewChange.complete();
  1416. this._destroyed.next();
  1417. this._destroyed.complete();
  1418. for (let view of this._templateCache) {
  1419. view.destroy();
  1420. }
  1421. }
  1422. /**
  1423. * React to scroll state changes in the viewport.
  1424. * @private
  1425. * @return {?}
  1426. */
  1427. _onRenderedDataChange() {
  1428. if (!this._renderedRange) {
  1429. return;
  1430. }
  1431. this._renderedItems = this._data.slice(this._renderedRange.start, this._renderedRange.end);
  1432. if (!this._differ) {
  1433. this._differ = this._differs.find(this._renderedItems).create(this.cdkVirtualForTrackBy);
  1434. }
  1435. this._needsUpdate = true;
  1436. }
  1437. /**
  1438. * Swap out one `DataSource` for another.
  1439. * @private
  1440. * @param {?} oldDs
  1441. * @param {?} newDs
  1442. * @return {?}
  1443. */
  1444. _changeDataSource(oldDs, newDs) {
  1445. if (oldDs) {
  1446. oldDs.disconnect(this);
  1447. }
  1448. this._needsUpdate = true;
  1449. return newDs ? newDs.connect(this) : of();
  1450. }
  1451. /**
  1452. * Update the `CdkVirtualForOfContext` for all views.
  1453. * @private
  1454. * @return {?}
  1455. */
  1456. _updateContext() {
  1457. /** @type {?} */
  1458. const count = this._data.length;
  1459. /** @type {?} */
  1460. let i = this._viewContainerRef.length;
  1461. while (i--) {
  1462. /** @type {?} */
  1463. let view = (/** @type {?} */ (this._viewContainerRef.get(i)));
  1464. view.context.index = this._renderedRange.start + i;
  1465. view.context.count = count;
  1466. this._updateComputedContextProperties(view.context);
  1467. view.detectChanges();
  1468. }
  1469. }
  1470. /**
  1471. * Apply changes to the DOM.
  1472. * @private
  1473. * @param {?} changes
  1474. * @return {?}
  1475. */
  1476. _applyChanges(changes) {
  1477. // Rearrange the views to put them in the right location.
  1478. changes.forEachOperation((/**
  1479. * @param {?} record
  1480. * @param {?} adjustedPreviousIndex
  1481. * @param {?} currentIndex
  1482. * @return {?}
  1483. */
  1484. (record, adjustedPreviousIndex, currentIndex) => {
  1485. if (record.previousIndex == null) { // Item added.
  1486. // Item added.
  1487. /** @type {?} */
  1488. const view = this._insertViewForNewItem((/** @type {?} */ (currentIndex)));
  1489. view.context.$implicit = record.item;
  1490. }
  1491. else if (currentIndex == null) { // Item removed.
  1492. this._cacheView(this._detachView((/** @type {?} */ (adjustedPreviousIndex))));
  1493. }
  1494. else { // Item moved.
  1495. // Item moved.
  1496. /** @type {?} */
  1497. const view = (/** @type {?} */ (this._viewContainerRef.get((/** @type {?} */ (adjustedPreviousIndex)))));
  1498. this._viewContainerRef.move(view, currentIndex);
  1499. view.context.$implicit = record.item;
  1500. }
  1501. }));
  1502. // Update $implicit for any items that had an identity change.
  1503. changes.forEachIdentityChange((/**
  1504. * @param {?} record
  1505. * @return {?}
  1506. */
  1507. (record) => {
  1508. /** @type {?} */
  1509. const view = (/** @type {?} */ (this._viewContainerRef.get((/** @type {?} */ (record.currentIndex)))));
  1510. view.context.$implicit = record.item;
  1511. }));
  1512. // Update the context variables on all items.
  1513. /** @type {?} */
  1514. const count = this._data.length;
  1515. /** @type {?} */
  1516. let i = this._viewContainerRef.length;
  1517. while (i--) {
  1518. /** @type {?} */
  1519. const view = (/** @type {?} */ (this._viewContainerRef.get(i)));
  1520. view.context.index = this._renderedRange.start + i;
  1521. view.context.count = count;
  1522. this._updateComputedContextProperties(view.context);
  1523. }
  1524. }
  1525. /**
  1526. * Cache the given detached view.
  1527. * @private
  1528. * @param {?} view
  1529. * @return {?}
  1530. */
  1531. _cacheView(view) {
  1532. if (this._templateCache.length < this.cdkVirtualForTemplateCacheSize) {
  1533. this._templateCache.push(view);
  1534. }
  1535. else {
  1536. /** @type {?} */
  1537. const index = this._viewContainerRef.indexOf(view);
  1538. // It's very unlikely that the index will ever be -1, but just in case,
  1539. // destroy the view on its own, otherwise destroy it through the
  1540. // container to ensure that all the references are removed.
  1541. if (index === -1) {
  1542. view.destroy();
  1543. }
  1544. else {
  1545. this._viewContainerRef.remove(index);
  1546. }
  1547. }
  1548. }
  1549. /**
  1550. * Inserts a view for a new item, either from the cache or by creating a new one.
  1551. * @private
  1552. * @param {?} index
  1553. * @return {?}
  1554. */
  1555. _insertViewForNewItem(index) {
  1556. return this._insertViewFromCache(index) || this._createEmbeddedViewAt(index);
  1557. }
  1558. /**
  1559. * Update the computed properties on the `CdkVirtualForOfContext`.
  1560. * @private
  1561. * @param {?} context
  1562. * @return {?}
  1563. */
  1564. _updateComputedContextProperties(context) {
  1565. context.first = context.index === 0;
  1566. context.last = context.index === context.count - 1;
  1567. context.even = context.index % 2 === 0;
  1568. context.odd = !context.even;
  1569. }
  1570. /**
  1571. * Creates a new embedded view and moves it to the given index
  1572. * @private
  1573. * @param {?} index
  1574. * @return {?}
  1575. */
  1576. _createEmbeddedViewAt(index) {
  1577. // Note that it's important that we insert the item directly at the proper index,
  1578. // rather than inserting it and the moving it in place, because if there's a directive
  1579. // on the same node that injects the `ViewContainerRef`, Angular will insert another
  1580. // comment node which can throw off the move when it's being repeated for all items.
  1581. return this._viewContainerRef.createEmbeddedView(this._template, {
  1582. $implicit: (/** @type {?} */ (null)),
  1583. cdkVirtualForOf: this._cdkVirtualForOf,
  1584. index: -1,
  1585. count: -1,
  1586. first: false,
  1587. last: false,
  1588. odd: false,
  1589. even: false
  1590. }, index);
  1591. }
  1592. /**
  1593. * Inserts a recycled view from the cache at the given index.
  1594. * @private
  1595. * @param {?} index
  1596. * @return {?}
  1597. */
  1598. _insertViewFromCache(index) {
  1599. /** @type {?} */
  1600. const cachedView = this._templateCache.pop();
  1601. if (cachedView) {
  1602. this._viewContainerRef.insert(cachedView, index);
  1603. }
  1604. return cachedView || null;
  1605. }
  1606. /**
  1607. * Detaches the embedded view at the given index.
  1608. * @private
  1609. * @param {?} index
  1610. * @return {?}
  1611. */
  1612. _detachView(index) {
  1613. return (/** @type {?} */ (this._viewContainerRef.detach(index)));
  1614. }
  1615. }
  1616. CdkVirtualForOf.decorators = [
  1617. { type: Directive, args: [{
  1618. selector: '[cdkVirtualFor][cdkVirtualForOf]',
  1619. },] },
  1620. ];
  1621. /** @nocollapse */
  1622. CdkVirtualForOf.ctorParameters = () => [
  1623. { type: ViewContainerRef },
  1624. { type: TemplateRef },
  1625. { type: IterableDiffers },
  1626. { type: CdkVirtualScrollViewport, decorators: [{ type: SkipSelf }] },
  1627. { type: NgZone }
  1628. ];
  1629. CdkVirtualForOf.propDecorators = {
  1630. cdkVirtualForOf: [{ type: Input }],
  1631. cdkVirtualForTrackBy: [{ type: Input }],
  1632. cdkVirtualForTemplate: [{ type: Input }],
  1633. cdkVirtualForTemplateCacheSize: [{ type: Input }]
  1634. };
  1635. /**
  1636. * @fileoverview added by tsickle
  1637. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1638. */
  1639. class ScrollingModule {
  1640. }
  1641. ScrollingModule.decorators = [
  1642. { type: NgModule, args: [{
  1643. imports: [BidiModule, PlatformModule],
  1644. exports: [
  1645. BidiModule,
  1646. CdkFixedSizeVirtualScroll,
  1647. CdkScrollable,
  1648. CdkVirtualForOf,
  1649. CdkVirtualScrollViewport,
  1650. ],
  1651. declarations: [
  1652. CdkFixedSizeVirtualScroll,
  1653. CdkScrollable,
  1654. CdkVirtualForOf,
  1655. CdkVirtualScrollViewport,
  1656. ],
  1657. },] },
  1658. ];
  1659. /**
  1660. * @deprecated ScrollDispatchModule has been renamed to ScrollingModule.
  1661. * \@breaking-change 8.0.0 delete this alias
  1662. */
  1663. class ScrollDispatchModule {
  1664. }
  1665. ScrollDispatchModule.decorators = [
  1666. { type: NgModule, args: [{
  1667. imports: [ScrollingModule],
  1668. exports: [ScrollingModule],
  1669. },] },
  1670. ];
  1671. /**
  1672. * @fileoverview added by tsickle
  1673. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1674. */
  1675. /**
  1676. * Time in ms to throttle the resize events by default.
  1677. * @type {?}
  1678. */
  1679. const DEFAULT_RESIZE_TIME = 20;
  1680. /**
  1681. * Simple utility for getting the bounds of the browser viewport.
  1682. * \@docs-private
  1683. */
  1684. class ViewportRuler {
  1685. /**
  1686. * @param {?} _platform
  1687. * @param {?} ngZone
  1688. */
  1689. constructor(_platform, ngZone) {
  1690. this._platform = _platform;
  1691. ngZone.runOutsideAngular((/**
  1692. * @return {?}
  1693. */
  1694. () => {
  1695. this._change = _platform.isBrowser ?
  1696. merge(fromEvent(window, 'resize'), fromEvent(window, 'orientationchange')) :
  1697. of();
  1698. // Note that we need to do the subscription inside `runOutsideAngular`
  1699. // since subscribing is what causes the event listener to be added.
  1700. this._invalidateCache = this.change().subscribe((/**
  1701. * @return {?}
  1702. */
  1703. () => this._updateViewportSize()));
  1704. }));
  1705. }
  1706. /**
  1707. * @return {?}
  1708. */
  1709. ngOnDestroy() {
  1710. this._invalidateCache.unsubscribe();
  1711. }
  1712. /**
  1713. * Returns the viewport's width and height.
  1714. * @return {?}
  1715. */
  1716. getViewportSize() {
  1717. if (!this._viewportSize) {
  1718. this._updateViewportSize();
  1719. }
  1720. /** @type {?} */
  1721. const output = { width: this._viewportSize.width, height: this._viewportSize.height };
  1722. // If we're not on a browser, don't cache the size since it'll be mocked out anyway.
  1723. if (!this._platform.isBrowser) {
  1724. this._viewportSize = (/** @type {?} */ (null));
  1725. }
  1726. return output;
  1727. }
  1728. /**
  1729. * Gets a ClientRect for the viewport's bounds.
  1730. * @return {?}
  1731. */
  1732. getViewportRect() {
  1733. // Use the document element's bounding rect rather than the window scroll properties
  1734. // (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE where window scroll
  1735. // properties and client coordinates (boundingClientRect, clientX/Y, etc.) are in different
  1736. // conceptual viewports. Under most circumstances these viewports are equivalent, but they
  1737. // can disagree when the page is pinch-zoomed (on devices that support touch).
  1738. // See https://bugs.chromium.org/p/chromium/issues/detail?id=489206#c4
  1739. // We use the documentElement instead of the body because, by default (without a css reset)
  1740. // browsers typically give the document body an 8px margin, which is not included in
  1741. // getBoundingClientRect().
  1742. /** @type {?} */
  1743. const scrollPosition = this.getViewportScrollPosition();
  1744. const { width, height } = this.getViewportSize();
  1745. return {
  1746. top: scrollPosition.top,
  1747. left: scrollPosition.left,
  1748. bottom: scrollPosition.top + height,
  1749. right: scrollPosition.left + width,
  1750. height,
  1751. width,
  1752. };
  1753. }
  1754. /**
  1755. * Gets the (top, left) scroll position of the viewport.
  1756. * @return {?}
  1757. */
  1758. getViewportScrollPosition() {
  1759. // While we can get a reference to the fake document
  1760. // during SSR, it doesn't have getBoundingClientRect.
  1761. if (!this._platform.isBrowser) {
  1762. return { top: 0, left: 0 };
  1763. }
  1764. // The top-left-corner of the viewport is determined by the scroll position of the document
  1765. // body, normally just (scrollLeft, scrollTop). However, Chrome and Firefox disagree about
  1766. // whether `document.body` or `document.documentElement` is the scrolled element, so reading
  1767. // `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding rect of
  1768. // `document.documentElement` works consistently, where the `top` and `left` values will
  1769. // equal negative the scroll position.
  1770. /** @type {?} */
  1771. const documentElement = (/** @type {?} */ (document.documentElement));
  1772. /** @type {?} */
  1773. const documentRect = documentElement.getBoundingClientRect();
  1774. /** @type {?} */
  1775. const top = -documentRect.top || document.body.scrollTop || window.scrollY ||
  1776. documentElement.scrollTop || 0;
  1777. /** @type {?} */
  1778. const left = -documentRect.left || document.body.scrollLeft || window.scrollX ||
  1779. documentElement.scrollLeft || 0;
  1780. return { top, left };
  1781. }
  1782. /**
  1783. * Returns a stream that emits whenever the size of the viewport changes.
  1784. * @param {?=} throttleTime Time in milliseconds to throttle the stream.
  1785. * @return {?}
  1786. */
  1787. change(throttleTime = DEFAULT_RESIZE_TIME) {
  1788. return throttleTime > 0 ? this._change.pipe(auditTime(throttleTime)) : this._change;
  1789. }
  1790. /**
  1791. * Updates the cached viewport size.
  1792. * @private
  1793. * @return {?}
  1794. */
  1795. _updateViewportSize() {
  1796. this._viewportSize = this._platform.isBrowser ?
  1797. { width: window.innerWidth, height: window.innerHeight } :
  1798. { width: 0, height: 0 };
  1799. }
  1800. }
  1801. ViewportRuler.decorators = [
  1802. { type: Injectable, args: [{ providedIn: 'root' },] },
  1803. ];
  1804. /** @nocollapse */
  1805. ViewportRuler.ctorParameters = () => [
  1806. { type: Platform },
  1807. { type: NgZone }
  1808. ];
  1809. /** @nocollapse */ ViewportRuler.ngInjectableDef = ɵɵdefineInjectable({ factory: function ViewportRuler_Factory() { return new ViewportRuler(ɵɵinject(Platform), ɵɵinject(NgZone)); }, token: ViewportRuler, providedIn: "root" });
  1810. /**
  1811. * \@docs-private \@deprecated \@breaking-change 8.0.0
  1812. * @param {?} parentRuler
  1813. * @param {?} platform
  1814. * @param {?} ngZone
  1815. * @return {?}
  1816. */
  1817. function VIEWPORT_RULER_PROVIDER_FACTORY(parentRuler, platform, ngZone) {
  1818. return parentRuler || new ViewportRuler(platform, ngZone);
  1819. }
  1820. /**
  1821. * \@docs-private \@deprecated \@breaking-change 8.0.0
  1822. * @type {?}
  1823. */
  1824. const VIEWPORT_RULER_PROVIDER = {
  1825. // If there is already a ViewportRuler available, use that. Otherwise, provide a new one.
  1826. provide: ViewportRuler,
  1827. deps: [[new Optional(), new SkipSelf(), ViewportRuler], Platform, NgZone],
  1828. useFactory: VIEWPORT_RULER_PROVIDER_FACTORY
  1829. };
  1830. /**
  1831. * @fileoverview added by tsickle
  1832. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1833. */
  1834. /**
  1835. * @fileoverview added by tsickle
  1836. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1837. */
  1838. export { _fixedSizeVirtualScrollStrategyFactory, FixedSizeVirtualScrollStrategy, CdkFixedSizeVirtualScroll, SCROLL_DISPATCHER_PROVIDER_FACTORY, DEFAULT_SCROLL_TIME, ScrollDispatcher, SCROLL_DISPATCHER_PROVIDER, CdkScrollable, ScrollingModule, ScrollDispatchModule, VIEWPORT_RULER_PROVIDER_FACTORY, DEFAULT_RESIZE_TIME, ViewportRuler, VIEWPORT_RULER_PROVIDER, CdkVirtualForOf, VIRTUAL_SCROLL_STRATEGY, CdkVirtualScrollViewport };
  1839. //# sourceMappingURL=scrolling.js.map