mirror of
https://github.com/akveo/ngx-admin.git
synced 2025-12-18 16:30:13 +01:00
fix(docs): add visibility service and ref fragment-target directive
This commit is contained in:
parent
6fff8623bc
commit
e77829a465
4 changed files with 227 additions and 62 deletions
|
|
@ -1,73 +1,97 @@
|
|||
import { Directive, ElementRef, Inject, Input, OnDestroy, OnInit, Renderer2 } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { NB_WINDOW } from '@nebular/theme';
|
||||
import { takeWhile, publish, refCount } from 'rxjs/operators';
|
||||
import { NgxTocElement, NgxTocStateService } from '../../services/toc-state.service';
|
||||
import { delay } from 'rxjs/internal/operators';
|
||||
import { Directive, ElementRef, Inject, Input, OnDestroy, OnInit, PLATFORM_ID, Renderer2 } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NB_WINDOW, NbLayoutScrollService } from '@nebular/theme';
|
||||
import { debounce, filter, publish, refCount, takeUntil, tap } from 'rxjs/operators';
|
||||
import { Subject, timer } from 'rxjs';
|
||||
|
||||
import { NgxVisibilityService } from '../../services/visibility.service';
|
||||
|
||||
const OBSERVER_OPTIONS = { rootMargin: '-100px 0px 0px' };
|
||||
|
||||
@Directive({
|
||||
// tslint:disable-next-line
|
||||
selector: '[ngxFragment]',
|
||||
})
|
||||
export class NgxFragmentTargetDirective implements OnInit, OnDestroy, NgxTocElement {
|
||||
export class NgxFragmentTargetDirective implements OnInit, OnDestroy {
|
||||
|
||||
private readonly marginFromTop = 120;
|
||||
private isCurrentlyViewed: boolean = false;
|
||||
private isScrolling: boolean = false;
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
@Input() ngxFragment: string;
|
||||
@Input() ngxFragmentClass: string;
|
||||
@Input() ngxFragmentSync: boolean = true;
|
||||
|
||||
private inView = false;
|
||||
private alive = true;
|
||||
private readonly marginFromTop = 120;
|
||||
|
||||
get fragment(): string {
|
||||
return this.ngxFragment;
|
||||
}
|
||||
|
||||
get element(): any {
|
||||
return this.el.nativeElement;
|
||||
}
|
||||
|
||||
get y(): number {
|
||||
return this.element.getBoundingClientRect().y;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
@Inject(NB_WINDOW) private window,
|
||||
private tocState: NgxTocStateService,
|
||||
private el: ElementRef,
|
||||
private el: ElementRef<HTMLElement>,
|
||||
private renderer: Renderer2,
|
||||
private router: Router,
|
||||
@Inject(PLATFORM_ID) private platformId,
|
||||
private visibilityService: NgxVisibilityService,
|
||||
private scrollService: NbLayoutScrollService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.ngxFragmentSync && this.tocState.add(this);
|
||||
|
||||
this.activatedRoute.fragment
|
||||
.pipe(publish(null), refCount(), takeWhile(() => this.alive), delay(10))
|
||||
.pipe(
|
||||
publish(null),
|
||||
refCount(),
|
||||
takeUntil(this.destroy$),
|
||||
filter(() => this.ngxFragmentSync),
|
||||
)
|
||||
.subscribe((fragment: string) => {
|
||||
if (fragment && this.fragment === fragment && !this.inView) {
|
||||
if (fragment && this.ngxFragment === fragment) {
|
||||
this.selectFragment();
|
||||
} else {
|
||||
this.deselectFragment();
|
||||
}
|
||||
});
|
||||
|
||||
this.visibilityService.isTopmostVisible(this.el.nativeElement, OBSERVER_OPTIONS)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((isTopmost: boolean) => {
|
||||
this.isCurrentlyViewed = isTopmost;
|
||||
if (isTopmost) {
|
||||
this.updateUrl();
|
||||
}
|
||||
});
|
||||
|
||||
this.scrollService.onScroll()
|
||||
.pipe(
|
||||
tap(() => this.isScrolling = true),
|
||||
debounce(() => timer(100)),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe(() => this.isScrolling = false);
|
||||
}
|
||||
|
||||
selectFragment() {
|
||||
this.ngxFragmentClass && this.renderer.addClass(this.el.nativeElement, this.ngxFragmentClass);
|
||||
this.setInView(true);
|
||||
this.window.scrollTo(0, this.el.nativeElement.offsetTop - this.marginFromTop);
|
||||
|
||||
const shouldScroll = !this.isCurrentlyViewed || !this.isScrolling;
|
||||
if (shouldScroll) {
|
||||
this.window.scrollTo(0, this.el.nativeElement.offsetTop - this.marginFromTop);
|
||||
}
|
||||
}
|
||||
|
||||
deselectFragment() {
|
||||
this.renderer.removeClass(this.el.nativeElement, this.ngxFragmentClass);
|
||||
}
|
||||
|
||||
setInView(val: boolean) {
|
||||
this.inView = val;
|
||||
updateUrl() {
|
||||
const urlFragment = this.activatedRoute.snapshot.fragment;
|
||||
const alreadyThere = urlFragment && urlFragment.includes(this.ngxFragment);
|
||||
if (!alreadyThere) {
|
||||
this.router.navigate([], { fragment: this.ngxFragment, replaceUrl: true });
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.alive = false;
|
||||
this.ngxFragmentSync && this.tocState.remove(this);
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
this.visibilityService.unobserve(this.el.nativeElement, OBSERVER_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue