1- import { Component , ElementRef , Inject , Input , OnInit } from '@angular/core' ;
1+ import { Component , ElementRef , Inject , Input , NgZone , OnDestroy , OnInit } from '@angular/core' ;
22import { DOCUMENT } from '@angular/platform-browser' ;
33import { ActivatedRoute , NavigationEnd , Router } from '@angular/router' ;
4- import 'rxjs/add/observable/fromEvent' ;
5- import 'rxjs/add/operator/debounceTime' ;
6- import 'rxjs/add/operator/takeUntil' ;
7- import { Observable } from 'rxjs/Observable' ;
4+ import { ScrollDispatcher , CdkScrollable } from '@angular/cdk/scrolling' ;
85import { Subject } from 'rxjs/Subject' ;
6+ import 'rxjs/add/operator/filter' ;
7+ import 'rxjs/add/operator/takeUntil' ;
98
109interface Link {
1110 /* id of the section*/
@@ -26,12 +25,11 @@ interface Link {
2625 styleUrls : [ './table-of-contents.scss' ] ,
2726 templateUrl : './table-of-contents.html' ,
2827} )
29- export class TableOfContents implements OnInit {
28+ export class TableOfContents implements OnDestroy , OnInit {
3029
31- @Input ( ) links : Link [ ] = [ ] ;
32- @Input ( ) container : string ;
3330 @Input ( ) headerSelectors = '.docs-markdown-h3,.docs-markdown-h4' ;
3431
32+ _links : Link [ ] = [ ] ;
3533 _activeLinkIndex : number ;
3634 _rootUrl : string ;
3735 private _scrollContainer : any ;
@@ -41,44 +39,49 @@ export class TableOfContents implements OnInit {
4139 constructor ( private _router : Router ,
4240 private _route : ActivatedRoute ,
4341 private _element : ElementRef ,
42+ private _scrollDispatcher : ScrollDispatcher ,
43+ private _ngZone : NgZone ,
4444 @Inject ( DOCUMENT ) private _document : Document ) {
4545
46- this . _router . events . takeUntil ( this . _destroyed ) . subscribe ( ( event ) => {
47- if ( event instanceof NavigationEnd ) {
46+ // Create new links and save root url at the end of navigation
47+ this . _router . events
48+ . filter ( event => event instanceof NavigationEnd )
49+ . takeUntil ( this . _destroyed )
50+ . subscribe ( event => {
4851 const rootUrl = _router . url . split ( '#' ) [ 0 ] ;
4952 if ( rootUrl !== this . _rootUrl ) {
50- this . links = this . createLinks ( ) ;
53+ this . _links = this . createLinks ( ) ;
5154 this . _rootUrl = rootUrl ;
5255 }
53- }
54- } ) ;
55-
56- this . _route . fragment . takeUntil ( this . _destroyed ) . subscribe ( fragment => {
57- this . _urlFragment = fragment ;
58- this . scrollFragmentIntoView ( ) ;
59- } ) ;
56+ } ) ;
57+
58+ // Scroll to section when the fragment changes
59+ this . _route . fragment
60+ . takeUntil ( this . _destroyed )
61+ . subscribe ( fragment => {
62+ this . _urlFragment = fragment ;
63+ this . scrollFragmentIntoView ( ) ;
64+ } ) ;
6065 }
6166
62- ngOnInit ( ) : void {
63- // On init, the sidenav content element doesn't yet exist, so it's not possible
64- // to subscribe to its scroll event until next tick (when it does exist).
65- Promise . resolve ( ) . then ( ( ) => {
66- this . _scrollContainer = this . container ?
67- this . _document . querySelectorAll ( this . container ) [ 0 ] : window ;
68-
69- Observable . fromEvent ( this . _scrollContainer , 'scroll' )
70- . takeUntil ( this . _destroyed )
71- . debounceTime ( 10 )
72- . subscribe ( ( ) => this . setActiveLink ( ) ) ;
73- } ) ;
67+ ngOnInit ( ) {
68+ // Update active link after scroll events
69+ this . _scrollDispatcher . scrolled ( )
70+ . takeUntil ( this . _destroyed )
71+ . subscribe ( scrollable =>
72+ this . _ngZone . run ( ( ) => {
73+ this . updateScrollContainer ( scrollable ) ;
74+ this . setActiveLink ( ) ;
75+ } ) ) ;
7476 }
7577
7678 ngOnDestroy ( ) : void {
7779 this . _destroyed . next ( ) ;
80+ this . _destroyed . complete ( ) ;
7881 }
7982
8083 updateScrollPosition ( ) : void {
81- this . links = this . createLinks ( ) ;
84+ this . _links = this . createLinks ( ) ;
8285 this . scrollFragmentIntoView ( ) ;
8386 }
8487
@@ -109,8 +112,8 @@ export class TableOfContents implements OnInit {
109112 }
110113
111114 private setActiveLink ( ) : void {
112- this . _activeLinkIndex = this . links
113- . findIndex ( ( link , i ) => this . isLinkActive ( link , this . links [ i + 1 ] ) ) ;
115+ this . _activeLinkIndex = this . _links
116+ . findIndex ( ( link , i ) => this . isLinkActive ( link , this . _links [ i + 1 ] ) ) ;
114117 }
115118
116119 private isLinkActive ( currentLink : any , nextLink : any ) : boolean {
@@ -134,4 +137,10 @@ export class TableOfContents implements OnInit {
134137 return 0 ;
135138 }
136139
140+ private updateScrollContainer ( scrollable : CdkScrollable | void ) : void {
141+ this . _scrollContainer = scrollable ?
142+ scrollable . getElementRef ( ) . nativeElement :
143+ window ;
144+ }
145+
137146}
0 commit comments