Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import {
- Component, Input, AfterViewInit, Inject, ElementRef, OnInit
- } from '@angular/core';
- import {DOCUMENT} from '@angular/platform-browser';
- import {Router, ActivatedRoute, Event, NavigationEnd} from '@angular/router';
- import {Observable} from 'rxjs/Observable';
- import {Subscription} from 'rxjs/Subscription';
- import 'rxjs/add/observable/fromEvent';
- @Component({
- selector: 'table-of-contents',
- styleUrls: ['./table-of-contents.scss'],
- template: `
- <div *ngIf="links?.length" class="toc-container">
- <h2>Contents</h2>
- <ul>
- <li *ngFor="let link of links; let i = index" class="li-{{link.type}}">
- <a [href]="_rootUrl + '#' + link.id"
- [class.active]="getLinkActive(link, i)">
- {{link.name}}
- </a>
- </li>
- </ul>
- </div>
- `
- })
- export class TableOfContents implements OnInit, AfterViewInit {
- @Input() links: any[] = [];
- @Input() topMargin = 0;
- @Input() container: string;
- _rootUrl: string;
- private _originalOffset: number;
- private _scrollElement: any;
- private _scrollSubscription: Subscription;
- private _routeSubscription: Subscription;
- private _offset: number;
- private _animationReq: any;
- private get scrollOffset(): number {
- if (typeof this._scrollElement.scrollTop !== 'undefined') {
- return this._scrollElement.scrollTop;
- } else if (typeof this._scrollElement.pageYOffset !== 'undefined') {
- return this._scrollElement.pageYOffset;
- }
- }
- constructor(private _router: Router,
- private _route: ActivatedRoute,
- private _element: ElementRef,
- @Inject(DOCUMENT) private _document: any) {
- this._routeSubscription = this._router.events.subscribe((event: Event) => {
- if (event instanceof NavigationEnd) {
- const rootUrl = _router.url.split('#')[0];
- if (rootUrl !== this._rootUrl) {
- this.links.splice(0, this.links.length);
- this.createLinks();
- this._rootUrl = rootUrl;
- }
- }
- });
- }
- ngOnInit(): void {
- this._originalOffset = this._element.nativeElement.getBoundingClientRect().top;
- this._scrollElement = this.container ?
- this._document.querySelectorAll(this.container)[0] : window;
- this._scrollSubscription = Observable.fromEvent(this._scrollElement, 'scroll')
- .subscribe(this.onScroll.bind(this));
- }
- ngAfterViewInit(): void {
- this._route.fragment.subscribe(fragment => {
- setTimeout(() => {
- const element: any = document.querySelector(`#${fragment}`);
- if (element) {
- element.scrollIntoView(element);
- }
- }, 500);
- });
- }
- ngOnDestroy(): void {
- this._scrollSubscription.unsubscribe();
- this._routeSubscription.unsubscribe();
- }
- createLinks(): void {
- // content is loading async, the timeout helps delay the query selector being empty
- setTimeout(() => {
- const headers = this._document.querySelectorAll('.docs-markdown-h3,.docs-markdown-h4');
- for (const header of headers) {
- const { top, height } = header.getBoundingClientRect();
- this.links.push({
- name: header.innerText,
- type: header.tagName.toLowerCase(),
- top: top - height,
- id: header.id
- });
- }
- }, 500);
- }
- onScroll(event: any): void {
- let scrollTop = event.target.scrollTop || this._scrollElement.scrollY;
- if (scrollTop < this._originalOffset) {
- scrollTop = 0;
- } else if (scrollTop > this._originalOffset) {
- scrollTop = (scrollTop + this.topMargin) - this._originalOffset;
- }
- cancelAnimationFrame(this._animationReq);
- this._animationReq = requestAnimationFrame(() => {
- // this._element.nativeElement.style.transform = `translateY(${scrollTop}px)`;
- });
- }
- getLinkActive(link: any, index: number): boolean {
- const nextLink = this.links[index + 1];
- if (this.scrollOffset >= (link.top - this.topMargin)) {
- if (nextLink && (nextLink.top - this.topMargin) < this.scrollOffset) {
- return false;
- }
- return true;
- }
- return false;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement