1
- import { Component , ElementRef , Inject , Input , OnInit } from '@angular/core' ;
1
+ import { Component , ElementRef , Inject , Input , NgZone , OnDestroy , OnInit } from '@angular/core' ;
2
2
import { DOCUMENT } from '@angular/platform-browser' ;
3
3
import { 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' ;
8
5
import { Subject } from 'rxjs/Subject' ;
6
+ import 'rxjs/add/operator/filter' ;
7
+ import 'rxjs/add/operator/takeUntil' ;
9
8
10
9
interface Link {
11
10
/* id of the section*/
@@ -26,12 +25,11 @@ interface Link {
26
25
styleUrls : [ './table-of-contents.scss' ] ,
27
26
templateUrl : './table-of-contents.html' ,
28
27
} )
29
- export class TableOfContents implements OnInit {
28
+ export class TableOfContents implements OnDestroy , OnInit {
30
29
31
- @Input ( ) links : Link [ ] = [ ] ;
32
- @Input ( ) container : string ;
33
30
@Input ( ) headerSelectors = '.docs-markdown-h3,.docs-markdown-h4' ;
34
31
32
+ _links : Link [ ] = [ ] ;
35
33
_activeLinkIndex : number ;
36
34
_rootUrl : string ;
37
35
private _scrollContainer : any ;
@@ -41,44 +39,49 @@ export class TableOfContents implements OnInit {
41
39
constructor ( private _router : Router ,
42
40
private _route : ActivatedRoute ,
43
41
private _element : ElementRef ,
42
+ private _scrollDispatcher : ScrollDispatcher ,
43
+ private _ngZone : NgZone ,
44
44
@Inject ( DOCUMENT ) private _document : Document ) {
45
45
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 => {
48
51
const rootUrl = _router . url . split ( '#' ) [ 0 ] ;
49
52
if ( rootUrl !== this . _rootUrl ) {
50
- this . links = this . createLinks ( ) ;
53
+ this . _links = this . createLinks ( ) ;
51
54
this . _rootUrl = rootUrl ;
52
55
}
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
+ } ) ;
60
65
}
61
66
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
+ } ) ) ;
74
76
}
75
77
76
78
ngOnDestroy ( ) : void {
77
79
this . _destroyed . next ( ) ;
80
+ this . _destroyed . complete ( ) ;
78
81
}
79
82
80
83
updateScrollPosition ( ) : void {
81
- this . links = this . createLinks ( ) ;
84
+ this . _links = this . createLinks ( ) ;
82
85
this . scrollFragmentIntoView ( ) ;
83
86
}
84
87
@@ -109,8 +112,8 @@ export class TableOfContents implements OnInit {
109
112
}
110
113
111
114
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 ] ) ) ;
114
117
}
115
118
116
119
private isLinkActive ( currentLink : any , nextLink : any ) : boolean {
@@ -134,4 +137,10 @@ export class TableOfContents implements OnInit {
134
137
return 0 ;
135
138
}
136
139
140
+ private updateScrollContainer ( scrollable : CdkScrollable | void ) : void {
141
+ this . _scrollContainer = scrollable ?
142
+ scrollable . getElementRef ( ) . nativeElement :
143
+ window ;
144
+ }
145
+
137
146
}
0 commit comments