Skip to content

Commit

Permalink
scroll to the first header
Browse files Browse the repository at this point in the history
  • Loading branch information
a1exymoroz committed Feb 9, 2024
1 parent a426572 commit 18d5d87
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 18 deletions.
50 changes: 43 additions & 7 deletions src/app/components/table-of-content/table-of-content.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { FlatTreeControl } from '@angular/cdk/tree';
import { Component, HostListener, Inject, Input } from '@angular/core';
import {
AfterViewInit,
Component,
HostListener,
Inject,
Input,
OnInit,
} from '@angular/core';
import {
MatTreeFlatDataSource,
MatTreeFlattener,
Expand All @@ -8,8 +15,9 @@ import {
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { HeaderNode, HeaderTreeNode } from '../../core/model/content.model';
import { RouterLink } from '@angular/router';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { DOCUMENT, NgClass } from '@angular/common';
import { first } from 'rxjs';

@Component({
selector: 'blog-table-of-content',
Expand All @@ -18,21 +26,21 @@ import { DOCUMENT, NgClass } from '@angular/common';
templateUrl: './table-of-content.component.html',
styleUrl: './table-of-content.component.scss',
})
export class TableOfContentComponent {
export class TableOfContentComponent implements AfterViewInit {
@Input() set headers(headers: HeaderNode[]) {
this.dataSource.data = headers;
this.treeControl.expandAll();
if (headers.length) {
this.createOffsets(headers);
this.activeHeader = this.offsetHeaders[0];
this.checkFirstHeader();
}
}

@Input() current!: string;

offsetHeaders: string[] = [];

activeHeader: string | null = null;
activeHeader = '';

@HostListener('document:scroll', ['$event'])
public onViewportScroll() {
Expand Down Expand Up @@ -68,7 +76,20 @@ export class TableOfContentComponent {
}
}

constructor(@Inject(DOCUMENT) private document: Document) {}
constructor(
@Inject(DOCUMENT)
private document: Document,
private route: ActivatedRoute
) {}

ngAfterViewInit(): void {
this.route.fragment.pipe(first()).subscribe(fragment => {
if (fragment) {
this.activeHeader = fragment;
this.checkFirstHeader();
}
});
}

private _transformer = (node: HeaderNode, level: number) => {
return {
Expand All @@ -95,7 +116,7 @@ export class TableOfContentComponent {

hasChild = (_: number, node: HeaderTreeNode) => node.expandable;

private createOffsets(data: HeaderNode[], array = this.offsetHeaders) {
private createOffsets(data: HeaderNode[], array = this.offsetHeaders): void {
for (const element of data) {
array.push(element.id);

Expand All @@ -104,4 +125,19 @@ export class TableOfContentComponent {
}
}
}

private checkFirstHeader(): void {
if (!this.offsetHeaders.length) {
return;
}

const offset = (
this.document.getElementById(this.activeHeader) as HTMLElement
).offsetTop;
if (this.offsetHeaders.includes(this.activeHeader) && offset) {
this.document.documentElement.scrollTo(0, offset);
} else {
this.activeHeader = this.offsetHeaders[this.offsetHeaders.length];
}
}
}
11 changes: 6 additions & 5 deletions src/app/features/post/post.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@ <h2>{{ post.excerpt }}</h2>
</header>
<div class="post__content">
<aside class="post__content-table">
<blog-table-of-content
class="sticky"
[headers]="(headers$ | async) || []"></blog-table-of-content>
@if (isContentReady) {
<blog-table-of-content
class="sticky"
[headers]="(headers$ | async) || []"></blog-table-of-content>
}
</aside>
<article class="post__content-text center-container">
<markdown
class="markdown-container"
[data]="markdown$ | async"
(ready)="resolveScripts()"
></markdown>
(ready)="resolveScripts()"></markdown>
</article>
</div>
}
Expand Down
22 changes: 16 additions & 6 deletions src/app/features/post/post.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ import {
ViewEncapsulation,
} from '@angular/core';
import { AsyncPipe, DOCUMENT, DatePipe } from '@angular/common';
import { Observable, distinctUntilChanged, filter, map, switchMap, tap, withLatestFrom } from 'rxjs';
import {
Observable,
distinctUntilChanged,
filter,
map,
switchMap,
tap,
withLatestFrom,
} from 'rxjs';
import { Post } from '../../core/model/post.model';
import { PostsService } from '../../core/services/posts.service';
import {
Expand Down Expand Up @@ -61,17 +69,17 @@ export class PostComponent {
post$: Observable<Post | undefined> = this.activatedRoute.url.pipe(
map(segments => segments.map(({ path }) => path).join('/')),
switchMap(permalink => this.postsService.getPost(permalink)),
tap((post) => {
tap(post => {
if (post) return;
this.notFound = true;
}),
})
);

markdown$: Observable<string> = this.activatedRoute.url.pipe(
map(segments => `${segments.map(({ path }) => path).join('/')}/post.md`),
switchMap(link => this.markdownService.getSource(link)),
map(this.removeMarkdownMetadataHeader),
distinctUntilChanged(),
distinctUntilChanged()
);

relatedPosts$: Observable<Post[]> = this.post$.pipe(
Expand All @@ -98,6 +106,7 @@ export class PostComponent {
);

notFound = false;
isContentReady = false;

constructor(
private postsService: PostsService,
Expand All @@ -106,7 +115,7 @@ export class PostComponent {
private markdownService: MarkdownService,
@Inject(DOCUMENT) private document: Document,
private cd: ChangeDetectorRef,
private htmlInMarkdownService: HtmlInMarkdownService,
private htmlInMarkdownService: HtmlInMarkdownService
) {}

navigate(path: string) {
Expand Down Expand Up @@ -160,5 +169,6 @@ export class PostComponent {

resolveScripts() {
this.htmlInMarkdownService.parseAll();
this.isContentReady = true;
}
}
}

0 comments on commit 18d5d87

Please sign in to comment.