Advertisement
Guest User

Untitled

a guest
Jul 24th, 2017
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.01 KB | None | 0 0
  1. import {
  2. Component, Input, AfterViewInit, Inject, ElementRef, OnInit
  3. } from '@angular/core';
  4. import {DOCUMENT} from '@angular/platform-browser';
  5. import {Router, ActivatedRoute, Event, NavigationEnd} from '@angular/router';
  6. import {Observable} from 'rxjs/Observable';
  7. import {Subscription} from 'rxjs/Subscription';
  8. import 'rxjs/add/observable/fromEvent';
  9.  
  10. @Component({
  11. selector: 'table-of-contents',
  12. styleUrls: ['./table-of-contents.scss'],
  13. template: `
  14. <div *ngIf="links?.length" class="toc-container">
  15. <h2>Contents</h2>
  16. <ul>
  17. <li *ngFor="let link of links; let i = index" class="li-{{link.type}}">
  18. <a [href]="_rootUrl + '#' + link.id"
  19. [class.active]="getLinkActive(link, i)">
  20. {{link.name}}
  21. </a>
  22. </li>
  23. </ul>
  24. </div>
  25. `
  26. })
  27. export class TableOfContents implements OnInit, AfterViewInit {
  28.  
  29. @Input() links: any[] = [];
  30. @Input() topMargin = 0;
  31. @Input() container: string;
  32.  
  33. _rootUrl: string;
  34. private _originalOffset: number;
  35. private _scrollElement: any;
  36. private _scrollSubscription: Subscription;
  37. private _routeSubscription: Subscription;
  38. private _offset: number;
  39. private _animationReq: any;
  40.  
  41. private get scrollOffset(): number {
  42. if (typeof this._scrollElement.scrollTop !== 'undefined') {
  43. return this._scrollElement.scrollTop;
  44. } else if (typeof this._scrollElement.pageYOffset !== 'undefined') {
  45. return this._scrollElement.pageYOffset;
  46. }
  47. }
  48.  
  49. constructor(private _router: Router,
  50. private _route: ActivatedRoute,
  51. private _element: ElementRef,
  52. @Inject(DOCUMENT) private _document: any) {
  53.  
  54. this._routeSubscription = this._router.events.subscribe((event: Event) => {
  55. if (event instanceof NavigationEnd) {
  56. const rootUrl = _router.url.split('#')[0];
  57. if (rootUrl !== this._rootUrl) {
  58. this.links.splice(0, this.links.length);
  59. this.createLinks();
  60. this._rootUrl = rootUrl;
  61. }
  62. }
  63. });
  64. }
  65.  
  66. ngOnInit(): void {
  67. this._originalOffset = this._element.nativeElement.getBoundingClientRect().top;
  68. this._scrollElement = this.container ?
  69. this._document.querySelectorAll(this.container)[0] : window;
  70. this._scrollSubscription = Observable.fromEvent(this._scrollElement, 'scroll')
  71. .subscribe(this.onScroll.bind(this));
  72. }
  73.  
  74. ngAfterViewInit(): void {
  75. this._route.fragment.subscribe(fragment => {
  76. setTimeout(() => {
  77. const element: any = document.querySelector(`#${fragment}`);
  78. if (element) {
  79. element.scrollIntoView(element);
  80. }
  81. }, 500);
  82. });
  83. }
  84.  
  85. ngOnDestroy(): void {
  86. this._scrollSubscription.unsubscribe();
  87. this._routeSubscription.unsubscribe();
  88. }
  89.  
  90. createLinks(): void {
  91. // content is loading async, the timeout helps delay the query selector being empty
  92. setTimeout(() => {
  93. const headers = this._document.querySelectorAll('.docs-markdown-h3,.docs-markdown-h4');
  94. for (const header of headers) {
  95. const { top, height } = header.getBoundingClientRect();
  96. this.links.push({
  97. name: header.innerText,
  98. type: header.tagName.toLowerCase(),
  99. top: top - height,
  100. id: header.id
  101. });
  102. }
  103. }, 500);
  104. }
  105.  
  106. onScroll(event: any): void {
  107. let scrollTop = event.target.scrollTop || this._scrollElement.scrollY;
  108.  
  109. if (scrollTop < this._originalOffset) {
  110. scrollTop = 0;
  111. } else if (scrollTop > this._originalOffset) {
  112. scrollTop = (scrollTop + this.topMargin) - this._originalOffset;
  113. }
  114.  
  115. cancelAnimationFrame(this._animationReq);
  116. this._animationReq = requestAnimationFrame(() => {
  117. // this._element.nativeElement.style.transform = `translateY(${scrollTop}px)`;
  118. });
  119. }
  120.  
  121. getLinkActive(link: any, index: number): boolean {
  122. const nextLink = this.links[index + 1];
  123.  
  124. if (this.scrollOffset >= (link.top - this.topMargin)) {
  125. if (nextLink && (nextLink.top - this.topMargin) < this.scrollOffset) {
  126. return false;
  127. }
  128. return true;
  129. }
  130. return false;
  131. }
  132.  
  133. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement